Monday 15 June 2015

c# - CurrentThreadTaskScheduler does not finish Synchronous -



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. awaits 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