单元测试和实体框架

11

我对EF非常陌生,想知道创建EF和SQL Server数据库的最佳方法。之后我想测试CRUD操作。EF是否以TDD方式实现,我对这些存储库模式、模拟上下文、伪造模式等感到困惑。

在EF中进行CRUD操作时,需要测试哪些内容?(DbContextSaveChanges()需要测试吗?)

那么如何使用Entity Framework进行单元测试呢?(我正在Visual Studio 2012、ASP.NET MVC4中检查所有这些内容)


2
只是提醒一下:这些通常被称为集成测试。如果它们是单元测试...你会每30秒点击“运行测试”(就像在TDD中应该做的那样),而每个测试对数据库的往返将使你的测试运行数分钟/数小时。实际与其他系统集成的集成测试是在发布前运行的。 - Simon Whitehead
@SimonWhitehead:那么为什么我们要使用存储库模式,它是否必需,模拟和伪造之间有什么区别? - neel
之后我想测试CRUD操作。你是想测试与CRUD操作相关的逻辑,还是想测试实体框架? - ta.speot.is
2
单元测试 Entity Framework 不是你的工作。 - ta.speot.is
4个回答

18

假设你有一个两层解决方案:

MyApp.Web

MyApp.Data

在你的数据层中,你会有类似这样的东西:

public class ProductsRepository : IProductsRepository
{
     public List<Product> GetAll()
     {
        //EF stuff 
        return _dbcontext.Products;
     }
} 

IProductsRepository是什么

public interface IProductsRepository
{
   List<Product> GetAll();
}

在MyApp.Web中的趋势是这样做。

public class ProductsController : Controller
{
    private readonly IProductsRepository _productsRepository;
    public ProductsController(IProductsRepository productsRepository)
    {
        _productsRepository = productsRepository;
    }

    public ActionResult Index(int page=1)
    {
        var allProducts = _productsRepository.GetAll();

        return View(allProducts)
    }
}

谁会在运行时将< strong>ProductsRepository放入构造函数中?人们使用类似< strong>Ninject 框架的依赖注入。但是为什么呢?因为这使它们能够伪造< strong>ProductsRepository 并进行测试。

public class FakeProductsRepository : IProductsRepository
{
     public List<Product> GetAll()
     {
        return new List<Product> 
           { 
              new Product { Name = "PASTE" }
              new Product { Name = "BRUSH" } 
           }, 
     }
} 

然后像这样对控制器进行单元测试

 [TestMethod]
 public void IndexGetsAllProducts()
 {
        //Arrange 
        var fakeProductRepo = new FakeProductsRepository();
        var productsController = new ProductsController(fakeProductRepo);

        //Act
        var result = productsController.Index(1) as ViewResult;

        //Assert
        var model = result.Model as List<Product>;
        Assert.AreEqual(2, model.Count);
 }

基本上,您正在伪造数据库,以便单元测试快速进行且独立于数据库。有时,人们使用模拟框架(如Moq)来进行模拟,其本质上是相同的。

如果要测试ProductsRepository,则不再称为单元测试,因为它依赖于外部源。对于这些测试,您实际上正在测试Entityframework。

人们结合使用像Specflow这样的框架进行集成测试。基本上,您可以使用真实的ProductsRepository实例化Productscontroller并检查返回的结果。


5
为了测试EF的功能,我建议针对已知数据编写集成测试。一种常见方法是在测试之前将数据作为先决条件构建,以测试基于选择的功能,如下:
1. 插入已知数据 2. 对已知数据运行选择功能 3. 断言结果
以上步骤将测试您的查询和EF绑定/模型。
从EF返回的数据上执行业务逻辑应该通过模拟来抽象EF逻辑。这将使您能够编写单元测试,重点测试逻辑而无需担心集成点/数据依赖性。

1
只是为了补充可能的第四点。将每个测试封装在一个事务中,在结束时回滚 - 这样,测试运行后您会保持数据库干净。 - Adrian Zanescu

5
仓储库和工作单元模式的目的是在应用程序的数据访问层和业务逻辑层之间创建一个抽象层。实施这些模式可以帮助隔离应用程序与数据存储中的更改,并促进自动化单元测试或测试驱动开发(TDD)。
只需点击此处了解说明和示例。

2
你可以尝试使用内存数据库来测试你的EF模型。这里有一个例子,使用Effort作为单元测试的数据库。请参考这里

1
链接已失效。 - Bert Sinnema

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接