简体   繁体   English

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

[英]How can I unit test an Asynchronous method in F#?

I've got a method that returns a task I'm trying to test synchonously in F#. 我有一个方法可以返回要在F#中同步测试的任务。 Here's the method, which is an implementation of a C# interface: 这是方法,它是C#接口的实现:

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

Here's my test logic 这是我的测试逻辑

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

The test runs, and with 5 items, the expected number of calls should be 5, but the test fails with a random number of calls between 1 and 2. 运行测试,并且有5个项目,预期的呼叫次数应为5,但测试失败,并且呼叫次数在1到2之间。

The same test in C# for another implementation is simply: 在C#中针对另一种实现的相同测试很简单:

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

How do I ensure the task is completed for the unit tests? 如何确保单元测试的任务完成?

How do I write the equivalent test method with async ? 如何使用async编写等效的测试方法?

Take a look at the await word in your C# code. 看一下C#代码中的await单词。 What do you think it does? 您觉得呢? What if you dropped it? 如果您放弃了该怎么办? Would the test still work? 该测试仍然有效吗?

The answers are: "It makes the async call part of the surrounding async computation" and "No, it wouldn't" 答案是:“它使异步调用成为周围异步计算的一部分”和“不,它不会”

It wouldn't, because the call to RunAsync would no longer be a part of the surrounding computation, and the surrounding computation wouldn't know that it had to "wait" for it to complete. 不会,因为对RunAsync的调用将不再是周围计算的一部分,并且周围计算也不知道它必须“等待”完成。

And this is exactly what you're doing in your F# code: you're just calling the function and forgetting about it, not making it part of the surrounding workflow. 这正是您在F#代码中所做的事情:您只是调用该函数而忘记了它,而不是使其成为周围工作流程的一部分。 The function goes off and starts working on its own, and your async workflow goes off in a different direction. 该功能将关闭并自行开始工作,并且async工作流朝另一个方向关闭。

In F#, the way to make nested calls a part of the surrounding flow is with let! 在F#中,让嵌套调用成为周围流程的一部分的方法是let! or do! 还是do! keywords, which are somewhat analogous to await in C#. 关键字,有些类似于在C#中await Like this: 像这样:

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

But in your case this wouldn't work right away, because RunAsync returns a C# Task , not an F# Async . 但是在您的情况下,这将无法立即RunAsync ,因为RunAsync返回C# Task ,而不是F# Async So you need to also convert from the former to the latter. 因此,您还需要从前者转换为后者。 To do the conversion, use the Async.AwaitTask function: 要进行转换,请使用Async.AwaitTask函数:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM