如何使用xunit验证模拟异步方法中的值?

时间:2018-03-27 13:20:55

标签: asp.net-mvc unit-testing task moq xunit

我希望能够在单元测试中验证传递给异步方法的参数。但由于该方法是异步的,执行断言的任务对整体的单元测试没有影响。断言的异常不会传递给单元测试框架(xunit)。我正在模拟我的存储库方法,这些方法由MVC IHostedService调用。

示例:

CancellationTokenSource sourceToken = new CancellationTokenSource();

var repositoryMock = new Mock<IRepository>();
repositoryMock.Setup(x => x.BulkUpdate(It.IsAny<IEnumerable<MyDocument>>(), It.IsAny<bool>()))
    .ReturnsAsync((IEnumerable<MyDocument> notifications, bool upsert) =>
{
    // verify all the event Ids are in the newly created Notifications (these will actually all be NULL since we can't set the eventIds in the Event object
    Assert.True(notifications.All(x => x.Detail != null));
    Assert.True(notifications.All(x => x.Map != null));

    sourceToken.Cancel();

    return notifications.Select(x => x.Id);
});

await Assert.ThrowsAsync<TaskCanceledException>(async () => await Task.Delay(20000, sourceToken.Token));
Assert.True(sourceToken.IsCancellationRequested);

此处的示例不包括启动MVC服务,但Startup将我的IHostedService创建为单例,它也会立即启动它。它最终将调用此BulkUpdate方法,并且我希望能够验证IHostedService是否使用正确的数据调用BulkUpdate。 CancellationTokenSource用于表示测试可以退出,因为IHostedService已经完成了我想要测试的工作。

后台服务的启动方式: 在单元测试启动类中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IHostedService, MyBackgroundService>();
}

我们有一个生成IWebHostBuilder的方法......

internal static IWebHostBuilder GetWebHostBuilder(Mock<IIRepository> repositoryMock = null)
{
    var environment = "Development"; //

    var builder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables();

    var configuration = builder.Build();

    var webHostBuilder = new WebHostBuilder()
            .UseEnvironment(environment)
            .UseConfiguration(configuration)
            .ConfigureServices(services =>
            {
                // Add the repository mocks and the type of user thats needed for authentications in the Controller being tested
                // These will be added before the IntegrationTestStartup.ConfigureTransientServices method runs and adds
                // the repository objects.

                if(repositoryMock != null)
                    services.AddTransient<IRepository>((y) => { return repositoryMock.Object; });
            })
            .UseStartup<UnitTestStartup>(); // This is the Startup of AHENS

    return webHostBuilder;
}

我们在单元测试中使用注入模拟存储库来测试后台服务......

TestServer testServer = new TestServer(TestBase.GetWebHostBuilder(repositoryMock));

这一切都“有效”,除非其中一个断言失败(抛出异常)。如何让断言汇总到单元测试以使测试失败?有没有更好的方法呢?我们有一些测试用例,我们想验证模拟的存储库方法是不是被调用,还是被多次调用。这是多线程应用程序问题的单元测试。

1 个答案:

答案 0 :(得分:0)

如果我理解正确,您试图断言BulkUpdate的参数是您所期望的,可以这样做......

repositoryMock.Verify(x => x.BulkUpdate(It.Is<IEnumerable<MyDocument>>(
    notifications=>notifications.All(n => n.Detail != null)), It.IsAny<bool>()));

然后类似于Map