【问题标题】:Running xUnit tests on Teamcity using async methods使用异步方法在 Teamcity 上运行 xUnit 测试
【发布时间】:2015-08-15 04:47:55
【问题描述】:

我进行了以下 xUnit 测试,它使用 HttpClient 在网络服务器上调用状态 api 方法。

[Fact]
public void AmIAliveTest()
{
    var server = TestServer.Create<Startup>();

    var httpClient = server.HttpClient;
    var response = httpClient.GetAsync("/api/status").Result;

    response.StatusCode.Should().Be(HttpStatusCode.OK);
    var resultString = response.Content.ReadAsAsync<string>().Result;

    resultString.Should().Be("I am alive!");
}

此测试在本地运行良好。但是当我提交代码并尝试在 TeamCity 构建服务器上运行相同的测试时,它会永远运行。我什至不得不杀死 xunit runner 进程,因为停止构建不会停止这个进程。

但是当我这样写测试时

[Fact]
public async void AmIAliveTest()
{
   var server = TestServer.Create<Startup>();

   var httpClient = server.HttpClient;
   var response = await httpClient.GetAsync("/api/status");

   response.StatusCode.Should().Be(HttpStatusCode.OK);
   var resultString = await response.Content.ReadAsAsync<string>();

   resultString.Should().Be("I am alive!");
}

它在本地和 TeamCity 上运行良好。

我现在担心的是,我忘记像第二个变体那样编写测试,并且有时 teamcity 构建会挂起。

谁能向我解释为什么在teamcity buildserver上运行的xUnit一开始就没有正确运行测试?有没有办法解决这个问题?

【问题讨论】:

    标签: c# async-await xunit.net teamcity-9.0


    【解决方案1】:

    谁能向我解释为什么在teamcity buildserver上运行的xUnit一开始就没有正确运行测试?

    首先,我会检查您的 xUnit 版本 - 您应该运行的是最近发布的 2.0。我怀疑你的本地版本可能已经过时了。

    核心问题在这一行:

    var resultString = response.Content.ReadAsAsync<string>().Result;
    

    我怀疑您遇到了我在博客中描述的deadlock situationHttpClient 在某些平台上的一些方法没有正确使用ConfigureAwait(false),因此会出现这种死锁。 xUnit 2.0 将单线程SynchronizationContext 安装到其所有单元测试中,这提供了死锁场景的另一半。

    正确的解决方案是将Result替换为await,并将单元测试方法的返回类型从void更改为Task

    【讨论】:

    • 非常感谢。在我使用 .Result 添加带有公共 void 方法的第 8 类后,我的测试挂起。您完成了对问题原因的 8 小时搜索。
    • 我在 gitlab 中运行测试时遇到了同样的问题。我想仅仅让所有测试都使用等待并返回一个任务是不够的。但是,被测试的代码(在不同的项目中)也应该避免在任何地方使用Result,对吧?
    • @betabandido:正确。
    • 与此相关,我通过在测试类中实现IDisposable 来拆除测试。问题是我需要使用只有异步接口的客户端删除测试期间创建的资源。鉴于我不能使用Result 并且Dispose 方法不能返回Task,那么在Dispose 方法中调用异步方法的正确(和安全)方法是什么?
    • @betabandido:我建议将您自己的问题作为问题提出。这将使许多其他潜在的回答者看到它。
    【解决方案2】:

    您的测试已损坏。

    xUnit 需要 Task 返回才能确定测试失败,更重要的是,它是异常冒泡返回到 xUnit 的句柄。

    拥有public async void,您就有了一个例外的孤立Task。由此产生的异常当然不会被处理,因此当然会炸毁整个过程。因此您的测试运行停止。

    您可以通过像这样编写所有测试来修复它...

    [Fact]
    public async Task AmIAliveTest()
    {
       var server = TestServer.Create<Startup>();
    
       var httpClient = server.HttpClient;
       var response = await httpClient.GetAsync("/api/status");
    
       response.StatusCode.Should().Be(HttpStatusCode.OK);
       var resultString = await response.Content.ReadAsAsync<string>();
    
       resultString.Should().Be("I am alive!");
    }
    

    【讨论】:

    • 这不是真的。 xUnit.net 在 2.0+ 中支持“async void”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-21
    • 1970-01-01
    • 2015-11-20
    相关资源
    最近更新 更多