[英]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.