【问题标题】:C# unit testing mocking service ignoring returnC#单元测试模拟服务忽略返回
【发布时间】:2023-04-08 14:59:01
【问题描述】:

我正在编写一些单元测试来测试控制器。在该控制器中有对服务的调用。在我的测试中,我想模拟服务调用并明确指定所需的返回。

当代码运行时,它进入控制器,当它到达服务时,我可以清楚地看到它正在使用我的模拟。但是,mock 的返回值始终为 null 而不是我指定的返回值(即expectedItem)。有什么想法吗?

针对控制器操作的单元测试

public class SearchIATeamControllerTests : IDisposable
{
    private readonly SearchIATeamController _sut;
    private IFixture _fixture;

    public SearchIATeamControllerTests()
    {
        _fixture = new Fixture().Customize(new AutoMoqCustomization());
        _sut = _fixture.Create<SearchIATeamController>();
    }


    [Fact]
    public async Task Get_team_name_with_ia_team_name_returns_ok()
    {
        // Arrange
        var expectedResultItem = _fixture.Create<SearchIATeamResponse>();

        var expectedItem = new List<SearchIATeamResponse>
        {
            expectedResultItem
        };

        var mockIATeamService = _fixture.Create<Mock<IIATeamSearchService>>();
        mockIATeamService.Setup(m => m.GetIATeamName(It.IsAny<string>())).ReturnsAsync(expectedItem);

        // Act
        var result = await _sut.Get("test").ConfigureAwait(false);
    }
} 

为简洁起见,名称已缩写。

被测系统

public async Task<ActionResult<SearchIATeamResponse>> Get([FromQuery] string iATeamName)
{   
    if (iATeamName == null)
    {
        return BadRequest();
    }

    try
    {
        var result = await _searchIATeamService.GetIATeamName(iATeamName);

        if (!result.Any())
        {
            return NotFound();
        }

        return Ok(result);

     }
     catch (ArgumentException ex)
     {
         return BadRequest(ex.Message);
     }      
}

【问题讨论】:

  • 这几乎总是来自您的Setup 嘲笑错误的事情。尝试将模拟设置为严格模式,看看是否能给你任何提示。
  • 您能否展示正在单元测试的代码,以便我们了解如何调用 GetIATeamName 方法。
  • @IanSoc 现已添加。
  • @StriplingWarrior 严格模式没有发现任何问题。
  • 是严格模式导致调用抛出异常,还是 mock 继续返回 null?

标签: c# moq autofixture


【解决方案1】:

这里的一个关键点是您正在使用 AutoFixture 创建您的 Mock 和您的服务。为了确保您的安装程序使用与注入服务的模拟实例相同的模拟实例,您可能需要在创建服务之前Freeze 模拟实例。

public class SearchIATeamControllerTests : IDisposable
{
    private readonly SearchIATeamController _sut;
    private readonly Mock<IIATeamSearchService> _mockIATeamService;
    private IFixture _fixture;

    public SearchIATeamControllerTests()
    {
        _fixture = new Fixture().Customize(new AutoMoqCustomization());
        _mockIATeamService = _fixture.Freeze<Mock<IIATeamSearchService>>();
        _sut = _fixture.Create<SearchIATeamController>();
    }


    [Fact]
    public async Task Get_team_name_with_ia_team_name_returns_ok()
    {
        // Arrange
        var expectedResultItem = _fixture.Create<SearchIATeamResponse>();

        var expectedItem = new List<SearchIATeamResponse>
        {
            expectedResultItem
        };

        _mockIATeamService.Setup(m => m.GetIATeamName(It.IsAny<string>())).ReturnsAsync(expectedItem);

        // Act
        var result = await _sut.Get("test").ConfigureAwait(false);
    }
} 

请注意,我捕获了Freeze 的结果,因此它更易于在测试方法中使用。这是我喜欢的一种模式,但如果您不希望有另一个私有字段挂起,理论上您可以在您的方法中继续使用_fixture.Create&lt;&gt;()

【讨论】:

  • 除了这行 _mockIATeamService.Create>();无法在其上调用 Create。
  • 抱歉,这是一个复制/粘贴错误。固定。
  • 不幸的是上面的代码也有同样的问题,它没有使用在expectedItem的mock中指定的返回值。
  • @LeedsWalker:你确定你使用了我这里的代码吗?如果您将_mockIATeamService 初始化放在_sut 初始化之后,就会发生您描述的行为。
  • 是的,你是对的,只要在模拟冻结之后创建控制器,它就可以正常工作。在我之前的示例中,我在构造函数中创建了控制器,并在稍后的测试中进行了模拟,这意味着它不起作用。
【解决方案2】:

问题是由订单引起的 - 控制器只能在模拟服务设置后声明,而不是在构造函数中。

【讨论】:

  • 我之前没用过 AutoFixture。乍一看,还看到你必须在模拟设置完成后声明控制器,在我看来非常不方便。测试课看起来好难。我使用 Moq 和 XUnit,以及 AutoSetup(我编写的一个 nuget 库)。 AutoSetup 可以为被测类创建模拟作为构造函数的一部分。顺序无关紧要。您可以在测试方法中设置模拟。使用这种方法的测试类要干净得多。只是想发表评论,以防您想查看其他库。 github.com/tukaya/autosetup
  • 这不符合我的经验。我有很多代码,我在测试设置方法中创建被测主题,然后只添加模拟行为。唯一重要的顺序是在创建控制器之前是否冻结模拟。
猜你喜欢
  • 2019-04-13
  • 2019-10-27
  • 1970-01-01
  • 2021-07-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-26
  • 2011-04-11
  • 2014-07-05
相关资源
最近更新 更多