【问题标题】:Moq testing Entity FrameworkMoq 测试实体框架
【发布时间】:2020-12-21 18:09:48
【问题描述】:

我是Entity FrameworkMoq testing 的新手。

下面是我的EF code

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{

    protected readonly DbContext Context;
    public Repository(DbContext context)
    {
        Context = context;
    }

    public TEntity Get(int id)
    {
      
        return Context.Set<TEntity>().Find(id);
    }

    public IEnumerable<TEntity> GetAll()
    {
       
        return Context.Set<TEntity>().ToList();
    }

    public void Add(TEntity entity)
    {
        Context.Set<TEntity>().Add(entity);
    }

    public void Remove(TEntity entity)
    {
        Context.Set<TEntity>().Remove(entity);
    }

}


public interface IRepository<TEntity> where TEntity : class
{

    TEntity Get(int id);
    IEnumerable<TEntity> GetAll();


    void Add(TEntity entity);

    void Remove(TEntity entity);

}


public interface IUnitOfWork : IDisposable
{

    int Complete();
}


public class UnitOfWork : IUnitOfWork
{


    private readonly EF_SalesOrdersDBContext _context;

    public CustomersRepository customersRepository { get; set; }

    public UnitOfWork(EF_SalesOrdersDBContext context)
    {
        _context = context;
        customersRepository = new CustomersRepository(_context);
    }



    public int Complete()
    {
        return _context.SaveChanges();
    }

    public void Dispose()
    {
        _context.Dispose();
    }
}

public class CustomersRepository : Repository<Customer>
{
    public CustomersRepository(EF_SalesOrdersDBContext context)
        :base(context)
    {
            
    }



}

program.cs 中的以下代码有效。

   UnitOfWork unitOfWork = new UnitOfWork(new EF_SalesOrdersDBContext());
        unitOfWork.customersRepository.Add(new Customer { CompanyName = "test2", FirstName = "John", LastName = "Barnes", AddressLine1 = "11 lfc", City = "Liverpool", County = "LFC", Phone = "444" });         
        unitOfWork.Complete();

我想使用Moq 进行测试。

这是我尝试过的:

 [TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
  
        var mockSet = new Mock<DbSet<Customer>>();

        var mockContext = new Mock<EF_SalesOrdersDBContext>();
        mockContext.Setup(m => m.Customers).Returns(mockSet.Object);


        var service = new CustomersRepository(mockContext.Object);

   

        service.Add(new Customer { CompanyName = "test2", FirstName = "John", LastName = "Barnes", AddressLine1 = "11 lfc", City = "Liverpool", County = "LFC", Phone = "444" });

        mockSet.Verify(m => m.Add(It.IsAny<Customer>()), Times.Once());
        mockContext.Verify(m => m.SaveChanges(), Times.Once());


    }

}

我得到错误:

消息: 测试方法 UnitTestProject1.UnitTest1.TestMethod1 抛出异常: System.NullReferenceException:对象引用未设置为对象的实例。堆栈跟踪: Repository`1.Add(TEntity entity) line 34 UnitTest1.TestMethod1() 第 26 行

【问题讨论】:

    标签: c# entity-framework moq


    【解决方案1】:

    模拟数据库上下文是一个坏主意 - 您可以简单地使用内存上下文并避免模拟所有内容。它将像真正的数据库一样工作。

    请参阅 Microsoft 的这些建议:https://docs.microsoft.com/en-us/ef/core/testing/

    这个问题有一些答案显示了如何制作内存上下文:Unit testing with EF Core and in memory database

    【讨论】:

    • 我认为 In Memory context 不支持如果使用 Sprocs 进行 CRUD 操作。
    • 当然,您需要模拟这些存储过程并将它们抽象到某种服务类后面。尽可能只使用实体框架将使您的测试生活更加轻松,但在某些极端情况下,需要进行高度优化或不断变化的存储过程。
    • 但我建议不要将存储过程用于 CRUD 操作,除非您遇到某种性能瓶颈。
    【解决方案2】:

    使用实体框架实现存储库模式的一个关键原因是启用单元测试。存储库成为单元测试的抽象点。基本上是这样想的:

    • 您相信 EF 是正确编写和测试的,以保证您会得到应有的结果。
    • 存储库仅用作中继,不包含高级业务逻辑。
    • 存储库可能强制执行的任何依赖于数据状态的低级规则都将被集成测试覆盖。这些测试将使用已知状态的快照数据库或内存数据库运行。

    因此,您的主要业务逻辑应该驻留在您的服务/控制器中,这就是您的单元测试的目标。 Mocks 是为您的存储库制作的,而不是 EF DbContexts/DbSets。

    因此,例如,给定您的结构,我将有一个 UnitOfWork 模拟,它断言是否进行了任何 Complete() 调用,然后模拟存储库以获取预期的方法以返回已知状态。

    【讨论】:

    • 存储库模式是一个热门话题。对我来说,这是一个冗余层,因为实体框架上下文本身就是存储库。然后,您在生成必要的数据库上下文的数据库上下文工厂之上构建您的服务类。但这是一个见仁见智的问题,我看到人们在两个方向都争论得很好。
    • 就是这样。 :) IMO 它可以用于正确的原因,也可以用于错误的原因。它可以使单元测试更简单,因为模拟存储库比模拟 DbContext 更容易。尽管如果您的团队不使用/重视单元测试,它就没有什么价值。在我的书中,使用存储库尝试将 EF 隐藏在 Facade 后面是一个很大的反模式。它要么增加了很多的复杂性,要​​么削弱了 EF 可以提供的功能,或者两者兼而有之。通用存储库是我要注意的另一种代码异味。 (削弱能力)
    • 说得好,先生 :)
    • 数据库上下文是工作单元,集合是存储库。我个人使用存储库,因为它们为我提供了一个有用的抽象级别,例如用于实现缓存。
    猜你喜欢
    • 1970-01-01
    • 2012-10-19
    • 2019-07-07
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多