如何解决MOQ Setup使用构造函数参数获取dbContext
情况
我在这里尝试通过MOQ为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,TId> : IEntityRepository<TDbContext,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,而您尚未指定提供程序,而某些操作则需要该提供程序。
这可能是一个自以为是的话题,但是要解决您的问题,有多种方法可以解决:
- 将无参数构造函数添加到DbContext中进行测试。我不建议您这样做,因为我遵循不更改测试的SUT的口号。
- 使用内存提供程序; EF Core In-Memory Database Provider或SQLite EF Core Database Provider是我使用的两个。它们确实有局限性,但使用OP可能会很好,并解决了Microsoft关于您不应该mock the DbContext的注意事项。
- 使用现有的库,例如EntityFrameworkCore.Testing(免责声明,我是作者),该库将扩展内存提供程序以解决其局限性。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。