有没有一种使用.NET DI框架注入模拟存储库的好方法?

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

我有以下通用存储库:

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<>>());

你已经做到了吗/知道如何/相信这是可能实现的吗?

c# moq
2个回答
0
投票

为了实现这一点,您可以在测试项目中为测试服务设置单独的配置,在其中您可以用模拟版本替换通用存储库。具体方法如下:

为您的测试服务创建自定义配置: 在您的测试项目中,创建一个自定义配置,使用模拟的依赖项配置您的服务。您可以使用 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
}

通过使用这种方法,您可以在测试项目中配置服务以在需要时使用模拟存储库,并且无需为每个测试用例手动创建模拟存储库。这使您可以保持生产配置和测试配置之间的分离。

如果有帮助请告诉我!


0
投票

您要求的是一个自动模拟容器,但是也就是说,您不应该需要 DI 容器来进行单元测试

另一方面,覆盖整个应用程序的组成方式对于更粗粒度的测试(例如集成测试)非常有用。 以下是如何针对 ASP.NET 应用程序执行此操作的示例。然而,在这种情况下,我建议支持基于状态的测试而不是基于交互的测试,因此使用假对象而不是动态模拟库。

© www.soinside.com 2019 - 2024. All rights reserved.