c# - CurrentThreadTaskScheduler does not finish Synchronous -
i seek write unit-test view model, got stuck when trying verify icommand calls asynchronous method twice.
i utilize moq dependencies. set async method this.
this.communicationservicefake .setup(x => x.writeparameterasync(it.isany<string>(), it.isany<object>())) .returnsasyncincomplete();
the extension returnsasyncincomplete not homecoming instantly @ await keyword, found here: async/await , code coverage
i utilize own tasksheduler ensure methods compete before task.factory.startnew returns.
task.factory.startnew(() => viewmodel.command.execute(null), cancellationtoken.none, taskcreationoptions.none, new currentthreadtaskscheduler ());
basically currentthreadtaskscheduler comes here: wait until task finish in unit test , this:
public class currentthreadtaskscheduler : taskscheduler { protected override void queuetask(task task) { this.tryexecutetask(task); } protected override bool tryexecutetaskinline(task task, bool waspreviouslyqueued) { homecoming this.tryexecutetask(task); } protected override ienumerable<task> getscheduledtasks() { yield break; } }
the command phone call next code:
await this.communicationservice.writeparameterasync("parameter1", true); await this.communicationservice.writeparameterasync("parameter2", true);
then verification:
this.communicationservicefake .verify(t => t.writeparameterasync("parameter1", true), times.once); this.communicationservicefake .verify(t => t.writeparameterasync("parameter2", true), times.once);
sometimes says 2nd phone call did not happen. if replace task.factory.startnew threadsleep ensure finished, works fine thought note seem right delay unit tests unnecessarily.
why currentthreadtaskscheduler allow task.factory.startnew homecoming before command.execute finished?
the getincompletetask
extension method in the linked blog post uses task.run
, hence introducing thread (and race condition).
the currentthreadtaskscheduler
work async void
methods if tasks completed, you're avoiding returnsasyncincomplete
.
here's what's happening:
execute
runs in currentthreadtaskscheduler
. await
s incomplete task running on threadpool. @ point, startnew
task complete. the thread pool thread finishes incomplete task, , executes continuations on current thread (the thread pool thread). what you're trying unit test async void
method total coverage (i.e., asynchronously). not easy.
recommended solution
use form of async
-aware icommand
such ones describe in my msdn article. can utilize await viewmodel.command.executeasync(null)
in unit test , not mess around custom task schedulers @ all.
also, asynchronous mocking can simplified; there's no need custom awaiter because .net framework has one: task.yield
. can replace code single extension method (untested):
public static ireturnsresult<tmock> returnsincompleteasync<tmock, tresult>(this ireturns<tmock, task<tresult>> mock, tresult value) tmock : class { homecoming mock.returns(async () => { await task.yield(); homecoming value; }); }
keeping async void
if want maintain existing icommand
implementations, , want unit test async void
methods, and want total code coverage, need custom synchronizationcontext
instead of custom taskscheduler
. easiest way install asyncex nuget package , utilize asynccontext
:
// blocks current thread until continuations have completed. asynccontext.run(() => viewmodel.command.execute(null));
also, recommend utilize task.yield
approach described above instead of custom-awaitable-on-a-thread-pool.
c# unit-testing task-parallel-library async-await
No comments:
Post a Comment