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

时间:2019-01-30 16:03:26

标签: unit-testing asynchronous f# nunit

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

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

这是我的测试逻辑

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

该测试运行,并且有5个项目,预期的呼叫次数应为5,但测试失败,随机呼叫次数为1至2。

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

[Test]
public async Task Should_do_something_in_an_asynchronous_method()
{
    var unitUnderTest = GetService();
    await unitUnderTest.RunAsync();
}

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

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

1 个答案:

答案 0 :(得分:4)

看看您的C#代码中的await字。您觉得呢?如果您放弃了该怎么办?测试仍然可以吗?

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

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

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

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

let f = async { ... }
let g = async {
    let! x = f
    printfn "f returned: %A" x
}

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

async {
  do! logFileManager.RunAsync() |> Async.AwaitTask
  Assert.Pass()
} |> Async.RunSynchronously