【发布时间】:2019-12-28 21:22:26
【问题描述】:
不久前,在我们的一个 (X) 单元测试中,我的一位同事写道:
[<Fact>]
let ``Green Flow tests`` () =
use factory = new WebAppFactory()
use client = factory.CreateClient()
Check.theGreenFlow client
|> Async.AwaitTask
|> Async.RunSynchronously
很惊讶,我想知道为什么我的同事强制调用 Async.RunSynchronously 而 XUnit 可以很好地处理 Task 和 Async 类型。
然后我试了一下:
[<Fact>]
let ``Green Flow tests`` () =
use factory = new WebAppFactory()
use client = factory.CreateClient()
// this btw returns Task<unit>
Check.theGreenFlow client
得到:
Rm.Bai.IntegrationTests.RetrievalWorkflow.Green Flow tests
System.AggregateException : One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (One or more errors occurred. (Cannot access a disposed object.
Object name: 'IServiceProvider'.))))))
我想“很公平,use 的范围在函数的底部结束,然后在 Task<unit> 由 XUnit 运行器处理时释放”。
尽管IDisposable 对象可能在上面示例中返回Task 的函数中被引用,但运行器在函数结束后运行任务,因此根据我的说法,Dispose 调用已经发生了解https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/resource-management-the-use-keyword:
它提供与
let绑定相同的功能,但在值超出范围时添加对Dispose的调用。请注意,编译器会在值上插入null检查,因此如果值为null,则不会尝试调用 Dispose。[...]
当您使用
use关键字时,Dispose在包含代码块的末尾被调用
对我来说,避免处置对象的正确方法是使用 task 或 async 计算表达式之类的计算表达式:
[<Fact>]
let ``Green Flow tests`` () =
task {
use factory = new WebAppFactory()
use client = factory.CreateClient()
do! Check.theGreenFlow client
}
因此,Dispose() 实际上是代码的时刻被明确定义。
并且不必像在 sn-p no 中那样强制测试同步运行。 1.
与下面的内容不同,它仍然会导致 Cannot access a disposed object. 错误:
[<Fact>]
let ``Green Flow tests`` () =
use factory = new WebAppFactory()
use client = factory.CreateClient()
async {
do! Check.theGreenFlow client |> Async.AwaitTask
}
类似于 sn-p 号。 2.
我对这个问题的理解正确吗?
【问题讨论】:
-
是的,你的理解是正确的。