最小起订量设置使用构造函数参数获取 dbContext

问题描述 投票:0回答:2

情况

我在这里,尝试使用最小起订量为我的

GroupService
编写一些单元测试。

为了创建我的

GroupService
的实例,我模拟了 4 个需要通过构造函数传递的接口。现在,在其中一个模拟 (
IGroupRepository
) 上,调用了一个名为
Context
的属性,我的想法是
SetupGet
这个属性,然后简单地返回一个
GroupUser
的假列表。但无论我尝试什么,我总是会遇到错误。

代码

public class GroupServiceTests
{
    private readonly GroupService _groupService;
    private readonly Mock<AppDbContext> _dbContext;
    private readonly Mock<IGroupRepository> _groupRepository;
    private readonly Mock<IComponentService> _componentService;
    private readonly Mock<IUserContextService> _userContextService;
    private readonly Mock<IModelEntityMapper<Group, Core.DbContexts.Entities.Group>> _mapper;

    public GroupServiceTests()
    {
        var groupUsersMock = CreateDbSetMock(GetFakeListOfGroupUsers());
        _dbContext = new Mock<AppDbContext>(new DbContextOptions<AppDbContext>());
        _dbContext.SetupGet(x => x.GroupUser).Returns(groupUsersMock.Object);

        _groupRepository = new Mock<IGroupRepository>();
        _groupRepository.SetupGet(repo => repo.Context).Returns(_dbContext.Object);

        _componentService = new Mock<IComponentService>();
        _userContextService = new Mock<IUserContextService>();
        _mapper = new Mock<IModelEntityMapper<Group, Core.DbContexts.Entities.Group>>();

        _groupService = new GroupService(_groupRepository.Object, _componentService.Object, _userContextService.Object, _mapper.Object);
    }
}

GroupService
中,这一行称为:

// _repository reffers to IGroupRepository
userIdsForContextReset.AddRange(_repository.Context.GroupUser.Where(x => groupIds.Contains(x.GroupId)).Select(x => x.UserId));

GroupRepository
EntityRepository
看起来像这样:

public interface IGroupRepository : IEntityRepository<AppDbContext, Group>
{
    List<GroupPermission> GetInheritedGroupPermissions(int groupId);
}

public class GroupRepository : EntityRepository<AppDbContext, Group>, IGroupRepository
{
    public GroupRepository(AppDbContext dbContext) : base(dbContext)
    {
    }

    public List<GroupPermission> GetInheritedGroupPermissions(int groupId)
    {
        // Removed for brevity
    }
}
public class EntityRepository<TDbContext, TEntity> : EntityRepository<TDbContext, TEntity, int>, IEntityRepository<TDbContext, TEntity>
     where TDbContext : DbContext
     where TEntity : class, IEntity<int>
{
    public EntityRepository(TDbContext dbContext) : base(dbContext)
    {
    }
}
public class EntityRepository<TDbContext, TEntity, TId> : IEntityRepository<TDbContext, TEntity, TId>
     where TDbContext : DbContext
     where TEntity : class, IEntity<TId>
     where TId : IComparable
{
    public EntityRepository(TDbContext context)
    {
        Context = context;
    }

    public TDbContext Context { get; }
}

最后但并非最不重要的一点是,

AppDbContext
SqlDbContext

public class AppDbContext : Shared.DbContexts.SqlDbContext
{
    public virtual DbSet<GroupUser> GroupUser { get; set; }

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }
}
public class SqlDbContext : DbContext
{
    public SqlDbContext(DbContextOptions options) : base(options)
    {
        ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
        ChangeTracker.StateChanged += ChangeTracker_StateChanged;
    }
}

错误

我收到的错误位于构造函数内第一行的

SqlDbContext
内,内容如下:

System.InvalidOperationException:“尚未为此 DbContext 配置数据库提供程序。可以通过重写 DbContext.OnConfiguring 方法或在应用程序服务提供程序上使用 AddDbContext 来配置提供程序。如果使用 AddDbContext,还请确保您的 DbContext 类型在其构造函数中接受 DbContextOptions 对象,并将其传递给 DbContext 的基本构造函数。'

我做错了什么?

c# mocking entity-framework-core moq xunit
2个回答
2
投票

当您模拟一个实现时,它会使用与提供的参数相匹配的构造函数来创建对象;它运行该代码。 此外,任何无法模拟的东西(不是虚拟的或抽象的)都将按原样运行。在这种情况下,您传入 DbContextOptions 并且尚未指定提供程序,而有些东西需要它。

这可能是一个固执己见的话题,但是要解决您的问题,您可以采取多种方法:

  1. 将无参数构造函数添加到 DbContext 中以进行测试。我不会推荐这样做,因为我遵循不更改 SUT 进行测试的原则。
  2. 使用内存提供程序; EF Core In-Memory Database ProviderSQLite EF Core Database Provider 是我使用过的两个。它们确实有局限性,但对于 OP 用法来说可能没问题,并且解决了 Microsoft 关于如何不应该模拟 DbContext 的注释。
  3. 使用现有的库,例如 EntityFrameworkCore.Testing(免责声明,我是作者),它将扩展内存提供程序以解决其局限性。

0
投票

我只是通过实例化来完成它

DbContextOptions<DbContext>

var dbContextMock = new Mock<MyDbContext>(new DbContextOptions<MyDbContext>());

然后像这样进行设置:

var userDbSetMock = new Mock<DbSet<User>>();

var dbContextMock
    .Setup(dbContext => dbContext.Users)
    .Returns(userDbSetMock.Object);
© www.soinside.com 2019 - 2024. All rights reserved.