如何在 F# 中对异步方法进行单元测试?

rec*_*ace 1 f# nunit unit-testing asynchronous

我有一个方法可以返回我试图在 F# 中同步测试的任务。这是方法,它是 C# 接口的实现:

member this.RunAsync(): System.Threading.Tasks.Task =             
    async{
        this._settings.LogSettings |> Seq.iter(fun settings -> this.clearLogs(settings)) |> ignore
        return ()
        } |> Async.StartAsTask :> _
Run Code Online (Sandbox Code Playgroud)

这是我的测试逻辑

Async.AwaitTask(logFileManager.RunAsync())|> Async.Ignore
// and I've tried
async {
  logFileManager.RunAsync() 
  Assert.Pass()
} |> Async.RunSynchronously
Run Code Online (Sandbox Code Playgroud)

测试运行,有 5 个项目,预期的调用次数应该是 5,但测试失败,调用次数在 1 到 2 之间。

在 C# 中对另一个实现的相同测试很简单:

[Test]
public async Task Should_do_something_in_an_asynchronous_method()
{
    var unitUnderTest = GetService();
    await unitUnderTest.RunAsync();
}
Run Code Online (Sandbox Code Playgroud)

如何确保完成单元测试的任务?

如何编写等效的测试方法async

Fyo*_*kin 5

查看awaitC# 代码中的单词。你认为它有什么作用?如果丢了怎么办?测试还能用吗?

答案是:“它使异步调用成为周围异步计算的一部分”和“不,它不会”

它不会,因为对 的调用RunAsync将不再是周围计算的一部分,并且周围计算不会知道它必须“等待”它完成。

这正是您在 F# 代码中所做的:您只是调用了该函数并忘记了它,而不是使其成为周围工作流的一部分。该功能关闭并开始自行工作,您的async工作流程朝着不同的方向发展。

在 F# 中,使嵌套调用成为周围流程的一部分的方法是 withlet!do!关键字,这有点类似于awaitC#。像这样:

let f = async { ... }
let g = async {
    let! x = f
    printfn "f returned: %A" x
}
Run Code Online (Sandbox Code Playgroud)

但是在您的情况下,这不会立即起作用,因为RunAsync返回的是 C# Task,而不是 F# Async。因此,您还需要从前者转换为后者。要进行转换,请使用以下Async.AwaitTask函数:

async {
  do! logFileManager.RunAsync() |> Async.AwaitTask
  Assert.Pass()
} |> Async.RunSynchronously
Run Code Online (Sandbox Code Playgroud)