初始化程序具有依赖项时F#XUnit测试死锁

时间:2019-03-29 08:58:51

标签: f# xunit

我在netcoreapp2.2 .net核心测试项目中的测试遇到问题。

在测试开始之前,我需要获取一些将在测试之间共享的数据。

但是,从命令行运行以下测试时,它将挂起。 像这样执行测试:

dotnet test --filter "Test async initialization"

错误代码如下:

let c = new HttpClient (BaseAddress = (Uri "https://swapi.co/api/people/1/"))    

let luke = 
    async {                
        return! c.GetStringAsync "" |> Async.AwaitTask        
    } |> Async.RunSynchronously

[<Fact>]
let ``Test async initialization`` () =  
    Assert.NotNull(luke)

如果我像这样将HttpClient的创建放入luke提取程序中,它将起作用:

let luke = 
    let c = new HttpClient (BaseAddress = (Uri "https://swapi.co/api/people/1/"))
    async {                
        return! c.GetStringAsync "" |> Async.AwaitTask        
    } |> Async.RunSynchronously

[<Fact>]
let ``Test async initialization`` () =  
    Assert.NotNull(luke)

这意味着我无法在不同的提取程序之间共享相同的HttpClient。

任何人都知道发生了什么,以及如何在多个功能之间共享同一客户端?

1 个答案:

答案 0 :(得分:2)

引起此问题的原因是“初始化”代码不是真正的初始化代码。这些只是两个静态字段,只有在请求时才会对其进行评估。如果您调试单元测试,您将看到cluke仅在执行到达第

行时执行
Assert.NotNull(luke)

如果使用类似JustDecompile的反编译器,则会看到模块的代码位于名为Tests$的静态类中,该类的静态构造函数将初始化其自己的cluke属性。 Test async initialization放在Tests类中,它具有自己的cluke属性,这些属性委派给Tests$类。

长话短说,直到请求luke的值之前,“初始化”代码都不会运行。我不知道为什么这最终会阻止测试,很可能与测试运行程序发生冲突。初始化代码不在初始化时运行就足够了。

要使初始化代码在应有的时间运行,可以使用“经典”测试类型:

namespace MyTests


open System
open Xunit
open System.Net.Http
open Xunit.Abstractions

type Tests() =

    static let c = new HttpClient (BaseAddress = (Uri "https://swapi.co/api/people/1/"))    

    static let luke = 
        async {                
            return! c.GetStringAsync "" |> Async.AwaitTask        
        } |> Async.RunSynchronously

    static do 
        //Pity we can't actually print here
        printfn "Even more initialization!"

    [<Fact>]
    let ``Test async initialization`` () =  
        Assert.NotNull(luke)

在这种情况下,静态绑定应按其应在任何测试之前执行,并且代码不会阻塞。初始化只会发生一次。

对于capture output,测试类构造函数应接受ITestOutputHelper参数。现在有了测试类,这很容易做到:

type Tests(output:ITestOutputHelper) =

    ...

    [<Fact>]
    let ``Test async initialization`` () =  
        Assert.NotNull(luke)
        output.WriteLine "It worked!"

每次测试初始化​​应放在do块中:

type Tests(output:ITestOutputHelper) =

    do
      output.WriteLine "This prints before each test"