2

I have a async function say DoWork() which does some work and stores the result in a file. So the return type is void. I need to write a unit test for this function and here comes my problem.

internal sealed class MainClass: InheritedClass { public override async void Start() { base.Start(); var result = await TaskEx.Run(() => DoSomeWork()); Store(result); } } 

In the unit test class if I have code like below it does not wait, it moves to the next line for execution which fails the test. So, how can I make it to WAIT until the base operation is completed.

public async Task StoreDataTest() { var t = Task.Factory.StartNew(MainClass.Start); t.Wait(); var data = UtilityClass.GetStoredData(); Assert.IsNotNull(data, "No data found"); } 

I cannot change the return type when overriding Start(). In my case the base class is inherited in many places and changing the return type of the virtual method in base class is not an option. So should i start using background worker and event-handler ? should i use this approach ?

We cannot use .NET 4.5 because we still support XP :( So, I am using BCL libraries on .net 4.0 to get async and await features..Recently, I came across some blog which suggested not to use backgroundworker because its obsolete in later .NET versions ?

List<EntityClass> result = null; var worker = new BackgroundWorker(); worker.DoWork += (sender, e) => { result = GetData(); }; worker.RunWorkerCompleted += (sender, eventArgs) => { if (result == null) { return; } Store(result); // Event used for Unit testing purpose if (DataStoredEvent != null) { DataStoredEvent(this, new EventArgs()); } }; worker.RunWorkerAsync(); 

Thanks

4
  • 2
    I see your problem - you have an async void method :) Just make sure the method returns Task, and you'll be fine (it gives you something to await/Wait). Commented Oct 15, 2015 at 15:14
  • 1
    Avoid async void, you have no way of waiting for it to complete. Commented Oct 15, 2015 at 15:16
  • 2
    I have a async function say DoWork() which does some work and stores the result in a file. So the return type is void. The latter does not in fact follow from the former. The fact that you have a function that does some work and stores the result in a file means that the return type should not be void, unless it's a synchronous function. Commented Oct 15, 2015 at 15:17
  • I cannot change the return type when overriding Start(). In my case the base class is inherited in many places and changing the return type of the virtual method in base class is not an option. So should i start using background worker and event-handler ? Commented Oct 16, 2015 at 7:06

1 Answer 1

3

As others have noted, the best solution is to change your method to return Task, which is the natural return type for an asynchronous method without a returned value:

internal sealed class MainClass: InheritedClass { public override async Task StartAsync() { base.Start(); var result = await TaskEx.Run(() => DoSomeWork()); Store(result); } } 

On a side note, I recommend that you not use TaskEx.Run in your business logic classes; that method is best called only from the UI layer.

Once your method is properly returning Task, unit testing is straightforward:

public async Task StoreDataTest() { await MainClass.StartAsync(); var data = UtilityClass.GetStoredData(); Assert.IsNotNull(data, "No data found"); } 

Update based on comments:

Changing the base class is the best solution. The fact that it's a hard solution doesn't change the fact that it's the best solution.

That said, if you don't want to implement the best solution, it is possible to unit test async void methods using the AsyncContext type from my AsyncEx library:

public void StoreDataTest() { AsyncContext.Run(() => MainClass.Start()); var data = UtilityClass.GetStoredData(); Assert.IsNotNull(data, "No data found"); } 

Note that the unit test method is actually synchronous because it blocks the unit test thread until the async void method completes. This is a dangerous practice and not recommended for actual application code, but works fine for unit tests (in other words, only do this at the entry point).

Sign up to request clarification or add additional context in comments.

1 Comment

Unfortunately I cannot change the return type when overriding Start() ? In my case the base class is inherited in many places and changing the return type of the virtual method in base class is not an option ...

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.