【问题标题】:How do I test Repository pattern in .NET Core如何在 .NET Core 中测试存储库模式
【发布时间】:2021-09-05 14:11:30
【问题描述】:

我正在尝试使用 Moq + Nunit 设置测试。

我正在使用存储库模式

public interface IRepository<T> where T : class
{
    Task<List<T>> FindByConditionAsync(Expression<Func<T, bool>> expression);
    Task<List<T>> GetAllAsync();
    //...
}

public class Repository<TEntity, TContext> : IRepository<TEntity> 
{
    //...Omitted other code from this snippet

    public async Task<List<TEntity>> FindByConditionAsync(Expression<Func<TEntity, bool>> expression)
    {
        return await _context.Set<TEntity>().Where(expression).ToListAsync();
    }
}

public interface IUserRepository : IRepository<User>
{

}

public class UserRepository : Repository<User, MyDatabase>, IUserRepository
{
    public UserRepository(MyDatabase context) : base(context)
    {
    }
}

我的测试文件

public class UserProviderTests
{
    private IMapper _mapper;
    private UserProvider _userProvider;
    private Mock<MyDatabase> _mockContext;
    private UserRepository _userRepository;
    
    [SetUp]
    public void Setup()
    {
        var user1Guid = Guid.NewGuid();
        var user2Guid = Guid.NewGuid();
        var data = new List<User>
        {
            new User
            {
                Id = user1Guid,
                FirstName = "Optimus", 
                LastName = "Prime"
            },
            new User
            {
                Id = user2Guid,
                FirstName = "John",
                LastName = "Doe",
            }
        }.AsQueryable();

        var mockSet = new Mock<DbSet<User>>();
        mockSet.As<IQueryable<User>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<User>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        var options = new DbContextOptionsBuilder<MyDatabase>()
            .UseSqlServer("fakestring")
            .Options;

        _mockContext = new Mock<MyDatabase>(options);

        _mockContext.Setup(c => c.Users).Returns(mockSet.Object);
        _userRepository = new UserRepository(_mockContext.Object);

        var config = new MapperConfiguration(cfg => {
            cfg.AddProfile<MappingProfile>();
        });
        _mapper = new Mapper(config);
        
        _userProvider = new UserProvider(_mapper, _userRepository);
    }

    [Test]
    public async Task GetUsers_ReturnsSingleUser()
    {
        var users = await _userProvider.FindUser("xxx");
        Assert.AreEqual("xxx", users.FirstOrDefault()?.FirstName);
    }
}

UserProvider 是我正在使用的一项服务,它使用 UserRepository 来执行 CRUD 操作。 但是当我尝试运行此测试时,我收到错误消息 -

System.ArgumentNullException:值不能为空。 (参数“来源”)
堆栈跟踪: Queryable.Where[TSource](IQueryable'1 源,Expression'1 谓词) Repository'2.FindByConditionAsync(Expression'1 表达式) 第48行

抱歉这个问题太长了,但我在这里遗漏了什么吗?我不明白为什么 TSource(USer object) 为空。

【问题讨论】:

  • IQueryables 很难模拟,所以如果你不想在那里重新实现很多逻辑,要么将其抽象出来,这样你就不必测试它,或者考虑使用 in-使用 EF Core 的内存数据库,而不是尝试模拟您的 DbSet。
  • 还有几个第三库已经解决了这个问题。仅举几例:Moq.EntityFrameworkCoreEntityFrameworkCore3MockEntityFrameworkCore.Testing

标签: c# unit-testing nunit moq repository-pattern


【解决方案1】:

在我看来,您应该为项目中的服务编写一个测试。实际上,您不应该在 RepositoryLayer 中编写任何业务,因为 Repository 中没有任何业务规则。如果您需要更多帮助,请与我联系。 dpournabi@gmail.com

【讨论】:

    猜你喜欢
    • 2019-08-10
    • 1970-01-01
    • 1970-01-01
    • 2018-08-16
    • 1970-01-01
    • 2019-02-25
    • 2021-06-16
    • 1970-01-01
    相关资源
    最近更新 更多