我有以下通用存储库:
public interface IRepository<T> where T : BaseEntity
{
IMongoQueryable Entities { get; }
Task<T> AddAsync(T entity, InsertOneOptions? options, CancellationToken cancellationToken = default);
//...
}
通过使用 .NET DI 方法,我可以将存储库动态注入到我的容器中并轻松使用它:
serviceCollection.AddScoped(typeof(Repository<>), typeof(IRepository<>));
效果很好。 但是,我正在尝试找到一种在测试用例中使用模拟存储库的好方法。 例如,我的测试场景中有一个服务提供商,我可以从依赖注入中受益来获取对象的实例。例如:
public class UserService : IUserService
{
private IMapper _mapper;
private IRepository<UserEntity> _userRepository;
private ValidatorService<UserInput> _userInputValidator;
public UserService(IMapper mapper, IRepository<UserEntity> userRepository, ValidatorService<UserInput> userInputValidator)
{
(_mapper, _userRepository, _userInputValidator) = (mapper, userRepository, userInputValidator);
}
}
//...
_userService = ServiceProvider.GetRequiredService<UserService>();
这也有效,我获得了服务类的正确实例并可以测试它。 但是,在我的测试用例中,我想避免每次都这样做:
_mapper = ServiceProvider.GetRequiredService<IMapper>();
// \/ here
_userRepositoryMock = new Mock<IRepository<UserEntity>>();
_userInputValidator = ServiceProvider.GetRequiredService<ValidatorService<UserInput>>();
_userService = new UserService(_mapper, _userRepositoryMock.Object, _userInputValidator);
我不想手动创建模拟,而是使用 DI 来确定模拟存储库的范围,并且每当我从特定容器请求 userService 时,我都会得到模拟版本而不是“原始”实现”。类似:
serviceCollection.AddScoped(typeof(IRepository<>), new Mock<Repository<>>());
你已经做到了吗/知道如何/相信这是可能实现的吗?
为了实现这一点,您可以在测试项目中为测试服务设置单独的配置,在其中您可以用模拟版本替换通用存储库。具体方法如下:
为您的测试服务创建自定义配置: 在您的测试项目中,创建一个自定义配置,使用模拟的依赖项配置您的服务。您可以使用 Moq 创建模拟实例。对于您的具体情况,您可以模拟 IRepository。
public class TestServiceConfiguration
{
public static void Configure(IServiceCollection serviceCollection)
{
// Register the mocked IRepository<T> for UserEntity
var userRepositoryMock = new Mock<IRepository<UserEntity>>();
userRepositoryMock.Setup(repo => repo.Entities).Returns(/* Your mocked data source */);
serviceCollection.AddScoped(typeof(IRepository<>), serviceProvider =>
{
var entityType = serviceProvider.GetRequiredService<IEntityType>().ClrType;
if (entityType == typeof(UserEntity))
{
return userRepositoryMock.Object;
}
// Add additional logic for other entity types if needed
return serviceProvider.GetRequiredService<IRepositoryFactory>().CreateRepository(entityType);
});
// Add other test-specific service registrations
}
}
在测试用例中使用测试配置: 在您的测试用例中,使用 TestServiceConfiguration 通过模拟存储库配置您的服务。您可以在运行测试之前执行此操作:
public class UserServiceTests
{
private ServiceProvider ServiceProvider;
private UserService _userService;
[SetUp]
public void SetUp()
{
var serviceCollection = new ServiceCollection();
// Add other services and configurations needed for your tests
// ...
// Use the test service configuration
TestServiceConfiguration.Configure(serviceCollection);
ServiceProvider = serviceCollection.BuildServiceProvider();
// Resolve the UserService with the mocked repository
_userService = ServiceProvider.GetRequiredService<UserService>();
}
// Your test cases here
}
通过使用这种方法,您可以在测试项目中配置服务以在需要时使用模拟存储库,并且无需为每个测试用例手动创建模拟存储库。这使您可以保持生产配置和测试配置之间的分离。
如果有帮助请告诉我!
您要求的是一个自动模拟容器,但是也就是说,您不应该需要 DI 容器来进行单元测试。
另一方面,覆盖整个应用程序的组成方式对于更粗粒度的测试(例如集成测试)非常有用。 以下是如何针对 ASP.NET 应用程序执行此操作的示例。然而,在这种情况下,我建议支持基于状态的测试而不是基于交互的测试,因此使用假对象而不是动态模拟库。