我正在尝试在测试中使用带有异步方法的reactiveui测试调度程序.
等待异步调用时,测试挂起.
根本原因似乎是异步方法中等待的命令.
[Fact]
public async Task Test()
=> await new TestScheduler().With(async scheduler =>
{
await SomeAsyncMethod();
// *** execution never gets here
Debugger.Break();
});
private async Task SomeAsyncMethod()
{
var command = ReactiveCommand.CreateFromTask(async () =>
{
await Task.Delay(100);
});
// *** this hangs
await command.Execute();
}
Run Code Online (Sandbox Code Playgroud)
如何与没有死锁的测试调度程序一起执行异步调用?
我正在使用reactiveui 9.4.1
编辑:
我已经尝试了WithAsync()Funks答案中建议的方法,但行为是一样的.
\n\n\n如何与测试调度程序结合进行异步调用?
\n
简而言之
\n\ncommand.Execute()是一个冷可观察量。您需要订阅它,而不是使用await.
鉴于您对 的兴趣TestScheduler,我认为您想测试一些涉及时间的东西。然而,从什么时候我应该关心日程安排部分来看:
\n\n\n通过“new Thread()”或“Task.Run”创建的线程无法在单元测试中控制。
\n
因此,例如,如果您想检查是否Task在 100 毫秒内完成,则必须等到异步方法完成。可以肯定的是,这不是那种测试TestScheduler目的。
稍微长一点的版本
\n\n目的TestScheduler是通过启动事物并验证特定时间点的状态来验证工作流程。由于我们只能操作 a 上的时间TestScheduler,因此您通常不希望等待真正的异步代码完成,因为无法快进实际计算或 I/O。请记住,它是关于验证工作流程:vm.A在 20 毫秒时有新值,因此vm.B应该在 120 毫秒时有新值,...
那么如何测试 SUT?
\n\n1\\ 您可以使用以下方式模拟异步方法scheduler.CreateColdObservable
public class ViewModelTests\n{\n [Fact]\n public void Test()\n {\n string observed = "";\n\n new TestScheduler().With(scheduler =>\n {\n var observable = scheduler.CreateColdObservable(\n scheduler.OnNextAt(100, "Done"));\n\n observable.Subscribe(value => observed = value);\n Assert.Equal("", observed);\n\n scheduler.AdvanceByMs(99);\n Assert.Equal("", observed);\n\n scheduler.AdvanceByMs(1);\n Assert.Equal("Done", observed);\n });\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这里我们基本上替换command.Execute()为var observablecreated on scheduler。
很明显,上面的示例相当简单,但是通过多个可观察量相互通知,这种测试可以提供有价值的见解,并在重构时提供安全网。
\n\n参考:
\n\n\n\nIScheduler2\\ 您可以明确引用
a) 使用RxApp提供的调度器
\n\npublic class MyViewModel : ReactiveObject\n{\n public string Observed { get; set; }\n\n public MyViewModel()\n {\n Observed = "";\n\n this.MyCommand = ReactiveCommand\n .CreateFromTask(SomeAsyncMethod);\n }\n\n public ReactiveCommand<Unit, Unit> MyCommand { get; }\n\n private async Task SomeAsyncMethod()\n {\n await RxApp.TaskpoolScheduler.Sleep(TimeSpan.FromMilliseconds(100));\n Observed = "Done";\n }\n}\n\npublic class ViewModelTests\n{\n [Fact]\n public void Test()\n {\n new TestScheduler().With(scheduler =>\n {\n var vm = new MyViewModel();\n\n vm.MyCommand.Execute().Subscribe();\n Assert.Equal("", vm.Observed);\n\n scheduler.AdvanceByMs(99);\n Assert.Equal("", vm.Observed);\n\n scheduler.AdvanceByMs(1);\n Assert.Equal("Done", vm.Observed);\n });\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n笔记
\n\nCreateFromTask创建一个ReactiveCommand具有异步执行逻辑的。无需将该Test方法定义为异步或等待TestScheduler.
With在扩展方法的范围内RxApp.TaskpoolScheduler== RxApp.MainThreadScheduler.new TestScheduler()
b) 通过构造函数注入管理您自己的调度程序
\n\npublic class MyViewModel : ReactiveObject\n{\n private readonly IScheduler _taskpoolScheduler;\n public string Observed { get; set; }\n\n public MyViewModel(IScheduler scheduler)\n {\n _taskpoolScheduler = scheduler;\n Observed = "";\n\n this.MyCommand = ReactiveCommand\n .CreateFromTask(SomeAsyncMethod);\n }\n\n public ReactiveCommand<Unit, Unit> MyCommand { get; }\n\n private async Task SomeAsyncMethod()\n {\n await _taskpoolScheduler.Sleep(TimeSpan.FromMilliseconds(100));\n Observed = "Done";\n }\n}\n\npublic class ViewModelTests\n{\n [Fact]\n public void Test()\n {\n new TestScheduler().With(scheduler =>\n {\n var vm = new MyViewModel(scheduler); ;\n\n vm.MyCommand.Execute().Subscribe();\n Assert.Equal("", vm.Observed);\n\n scheduler.AdvanceByMs(99);\n Assert.Equal("", vm.Observed);\n\n scheduler.AdvanceByMs(0);\n Assert.Equal("Done", vm.Observed);\n });\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n参考:
\n\n\n\n让我们引用来自 Haacked 的另一句话:
\n\n\n\n不幸的是,下一点很重要,它
\nTestScheduler不会扩展到现实生活中,因此您的恶作剧仅限于异步响应式代码。因此,如果您调用Thread.Sleep(1000)测试,该线程实际上将被阻塞一秒钟。但就测试调度程序而言,时间还没有过去。