【问题标题】:Unit tests not showing code coverage with async/await单元测试未显示异步/等待的代码覆盖率
【发布时间】:2018-06-05 10:51:38
【问题描述】:

我使用的是 vs2017,但我的所有单元测试都没有显示代码覆盖率。我正在使用 async/await 和 MOQ。

[TestMethod]
[TestCategory("SeriesRepository")]
public void GetSeriesAsyncShouldReturnSeriesId12345()
{
    var repositoryMock = new Mock<ISeriesRepository>();

    var seriesId = "12345";
    var channel = 75864;            

    var mockSeries = new Mock<ISeries>();
    mockSeries.SetupGet(x => x.SeriesId).Returns("12345");

    repositoryMock
        .Setup(x => x.GetSeriesAsync(seriesId, channel))
        .ReturnsAsync(mockSeries.Object);

    var result = repositoryMock.Object.GetSeriesAsync(seriesId, channel).Result;

    result.SeriesId.Should().Be(seriesId);
}

【问题讨论】:

  • 您似乎只是在调用模拟方法,这不会导致调用任何非测试代码。您希望此测试覆盖哪些代码,但未显示为已覆盖?
  • 您是否在问为什么您的单元测试代码 没有显示为已覆盖,或者为什么您的实际逻辑代码 未显示为已覆盖?如果是前者,请确保您的测试实际运行。如果是后者,在哪里你在调用你的实际逻辑代码?似乎您正在设置一个模拟,然后确保模拟的行为与您模拟的一样。您想在这里测试的实际逻辑代码是什么?

标签: c# unit-testing asynchronous moq


【解决方案1】:

第一个事实是,您实际上并没有在测试中测试任何内容,因为您只是创建模拟并调用模拟。

您只是在测试模拟框架是否像宣传的那样工作。

其次,测试也可以异步进行,以允许测试按顺序执行。

[TestMethod]
[TestCategory("SeriesRepository")]
public async Task GetSeriesAsyncShouldReturnSeriesId12345() {
    var repositoryMock = new Mock<ISeriesRepository>();

    var seriesId = "12345";
    var channel = 75864;            

    var mockSeries = new Mock<ISeries>();
    mockSeries.Setup(_ => _.SeriesId).Returns(seriesId);

    repositoryMock
        .Setup(_ => _.GetSeriesAsync(seriesId, channel))
        .ReturnsAsync(mockSeries.Object);

    var result = await repositoryMock.Object.GetSeriesAsync(seriesId, channel);

    result.SeriesId.Should().Be(seriesId);
}

假设发生的是,您模拟被测目标的依赖项以按预期运行,以验证某些期望的行为

假设我们想测试一个类的目标方法。类似的东西

public class SeriesService {
    private readonly ISeriesRepository repository;

    public SeriesService(ISeriesRepository repository
        this.repository = repository;
    }

    public async Task<ISeries> GetSeries(string seriesId, int channel) {
        var series = await repository.GetSeriesAsync(seriesId, channel);

        //...do some other stuff

        return series;
    }
}

一个示例测试将如下所示

[TestMethod]
[TestCategory("SeriesRepository")]
public async Task TargetMethodShouldReturnSeriesId12345() {
    //Assert
    var repositoryMock = new Mock<ISeriesRepository>();

    var seriesId = "12345";
    var channel = 75864;            

    var mockSeries = new Mock<ISeries>();
    mockSeries.Setup(_ => _.SeriesId).Returns(seriesId);

    repositoryMock
        .Setup(_ => _.GetSeriesAsync(seriesId, channel))
        .ReturnsAsync(mockSeries.Object);

    var target = new SeriesService(repositoryMock.Object);

    //Act
    var result = await target.GetSeries(seriesId, channel);

    //Assert
    result.Should().NotBeNull();
    result.SeriesId.Should().Be(seriesId);
}

【讨论】:

  • 我对 TargetClass 感到困惑。 TargetClass 将类似于我的 SeriesService 类。我的 SeriesService 类期望注入一个 ISeriesRepository。
  • @user3582849 这可能是正确的。显示该类的示例,以便我们可以为它制定一个独立的单元测试。
  • 谢谢。做到了。这被认为是存储库测试还是服务测试?我创建了一个名为 SeriesRepositoryTest 的测试类,如果这是测试存储库,我应该如何测试服务层?
  • @user3582849 这个单元测试单独测试SeriesService。在这种情况下,存储库是一个显式依赖项,它被模拟以允许单独测试被测系统,而不会影响使用实际存储库实现。
  • 这是一个 web api,我也有一个控制器。你对用这个进行单元测试有什么建议?编写一个单元测试来测试控制器是否有意义? ISeriesService 被注入到控制器中。
【解决方案2】:

无需等待您的 repositoryMock 设置完成。因此,您的测试用例在异步调用完成之前就结束了。

通过添加确保您等待:

repositryMock.setup(...).ReturnsAsync(...).GetAwaiter().GetResult();

或者

repositryMock.setup(...).ReturnsAsync(...).Result;

或者

repositryMock.setup(...).ReturnsAsync(...).Wait();

这应该可以解决您的问题。

【讨论】:

  • 你的意思是,除了阅读.Result之外,哪个是阻塞调用? (如果确实涉及任何任务,应该是阻塞调用)。
  • 是的,我们应该进行这个阻塞调用;以确保在下一次调用发生之前执行所有涉及的任务。如果你的测试方法是异步的,那么你可以等待它而不是阻塞调用。
猜你喜欢
  • 2012-01-18
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
相关资源
最近更新 更多