如何模拟具有大量构造函数参数的 Service?

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

我继承了一个asp.net web api项目,遵循正常的控制器-->服务-->存储库架构。这个项目没有单元测试,我正在尝试对其进行改造。

这个项目中的典型服务是这样的:

public class TeamService : ITeamService
{
    private readonly ITeamRepository _teamRepository;
    private readonly ILogger<ITeamService> _logger;
    private readonly IMapper _mapper;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IUserRepository _userRepository;
    private readonly IPlayerRepository _playerRepository;

    public TeamService(ITeamRepository teamRepository, ILogger<ITeamService> logger,IMapper mapper, IHttpContextAccessor httpContextAccessor, IUserRepository userRepository, IPlayerRepository playerRepository)
    {
        _teamRepository = teamRepository;
        _logger = logger;
        _mapper = mapper;
        _httpContextAccessor = httpContextAccessor;
        _userRepository = userRepository;
        _playerRepository = playerRepository;
    }

    //the rest of the service....
}

这里我们有六个存储库必须传递到此服务中。项目中没有任何代码创建过

TeamRepository
TeamService
。相反,
TeamController
看起来像:

[Route("api/[controller]")]
[ApiVersion("1.0")]
[ApiController]
public class TeamController : ControllerBase
{
    private readonly ITeamService _teamService;
    private readonly ISchoolService _schoolService;
    public TeamController(ITeamService teamService, ISchoolService schoolService)
    {
        _teamService = teamService;
        _schoolService = schoolService;
    }

    //the rest of the controller....
}

WebApi 自动创建此类并将所需的

TeamService
传递到构造函数中。

在网络应用程序设置中,我们有

builder.Services.RegisterRepository(Assembly.Load("MyNamespace.Repository"));
builder.Services.RegisterServices(Assembly.Load("MyNamespace.Service")

我猜 WebApi 会进行一些反射来加载这些程序集,查找所有服务和存储库的类型,当有人点击 api 端点时,WebApi 通过将构造函数参数匹配来创建所需的对象(服务、存储库和控制器)实现这些接口并用它们创建控制器的已知类型。


我的问题是你如何用Moq做类似的事情?我想为

TeamService
编写一个单元测试,并且必须手动创建所有模拟存储库(每个存储库本身都有需要的参数),这似乎非常乏味。我希望有类似的说法

  • 加载
    MyNamespace.Mock.Services
    MyNamespace.Mock.Repositories
  • 中的所有类型
  • 每当某些构造函数参数需要类型时,请使用这些程序集中的类型
  • (伪代码)
    var service = Mock.CreateByReflection<TeamService>()
  • service.TestSomeMethod()

模拟 WebApi 方便的自动对象创建的 Moq 方式是什么?

c# unit-testing asp.net-web-api reflection moq
1个回答
0
投票

我继承了一个asp.net web api项目,遵循普通的controller-->service-->repository架构

是的,不幸的是,这很常见,但可能会给您带来问题。这不是你的错,因为你继承了代码库,我只是指出这一点,因为它可能会导致维护方面的摩擦,包括添加自动化测试。在架构层面,这种实现的问题在于它违反了依赖倒置原则

这听起来可能很抽象,但具有实际意义。出现的一个常见问题与测试有关,测试编写者反复遇到“如何模拟”某些东西的问题,因为抽象依赖于难以测试的实现细节。

我指出这一点是因为,如果您在向此代码库添加测试的过程中感到“测试很糟糕”,那么错误在于应用程序架构,而不是单元测试。

总而言之,代码库展示了依赖注入构造函数的疯狂。再说一遍,这不是你的错,而是另一个避免继续前进的代码味道。

测试确实不应该执行以下操作,但在遗留代码场景中,您可能需要考虑建立一个 auto-mocking 容器 来进行测试。例如,您可以出于此目的将起订量和温莎城堡结合起来。虽然您可以使用与生产代码相同的依赖注入容器,但您不必这样做。

短语 auto-mocking 的适用范围足够广泛,网络搜索可以为您提供比上面链接更多的示例。

需要明确的是,问题和建议的解决方案与 Moq 或任何其他基于反射的 Test Double 库关系不大。这是与依赖项的管理和组合相关的问题,与定义测试替身的问题不同。

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