情况
我在这里,尝试使用最小起订量为我的
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 的基本构造函数。'
我做错了什么?
当您模拟一个实现时,它会使用与提供的参数相匹配的构造函数来创建对象;它运行该代码。 此外,任何无法模拟的东西(不是虚拟的或抽象的)都将按原样运行。在这种情况下,您传入 DbContextOptions 并且尚未指定提供程序,而有些东西需要它。
这可能是一个固执己见的话题,但是要解决您的问题,您可以采取多种方法:
我只是通过实例化来完成它
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);