Unit test for asynchronous code
Asked Answered
S

3

9

I have got some code, which is using HttpWebRequest class's .BeginGetResponse() method and it is asynchronous. Also, I am using Microsoft's Unit Test App project for testing application.

The problem is that test framework does not waiting for end of running asynchronous code, so I can not check its result.

How should I test asynchronous code using Unit Test App project? I'm not using async/await modificators.

Sain answered 19/6, 2013 at 8:12 Comment(5)
have you thoguht about using mocks for the web class/methods?Overtly
Unit testing other .Net specific code atleast like HttpWebRequest class may not sound good. Because you are just unit testing your module , thinking that what if the other module return valid value , invalid value and throw expcetion. So it would be better if you could mock the HttpWebRequest and just test your logic.Ethnocentrism
Yep, I am using mocks: server returns JSON to me, so I use mocks JSONs for passing it to the parsing module. But now I need to add queries caching to my API implementation, so I need to check how it's working without debugging each time.Sain
If your test requires outsides resources like a databases or a network connection it no longer a unit test but already an integration test. A unit test framework might not be the right tool.Flan
You should mock your objects. Check #2114197Sacha
E
5

Update Answer
The original answer was quite an old one, before async and await were prevalent. I'd now recommend using them, by writing something like:

[TestMethod]
public async Task RunTest()
{
    var result = await doAsyncStuff();
    // Expectations
}

There's a good article that covers this in depth Async Programming : Unit Testing Asynchronous Code

Old Answer

I would tend to something simple like using a polling loop and checking a flag that would be set in the async code or you could use reset events. A simple example using threads:

[TestMethod]
public void RunTest()
{
    ManualResetEvent done = new ManualResetEvent(false);
    Thread thread = new Thread(delegate() {
        // Do some stuff
        done.Set();
    });
    thread.Start();

    done.WaitOne();
}

You'll need to think about exceptions and use a try/finally and report the errors to do this properly, but you get the idea. This method however might not suit if you're doing lots of async stuff over and over, unless you fancy pushing this into a reusable method.

Eichler answered 19/6, 2013 at 8:22 Comment(4)
Good idea, that works great for me! Thank you :) P.S. you got error in while(asyncDone) it should negate condition: while(!asyncDone)Sain
Why poll when there are plenty of better ways to wait for asynchronous code to complete. (Manual/AutoResetEvent, Monitor, etc.) or better yet if you are going to use something like the above, why not just thread.Join()?Monah
To expand on comment from @Iridium: or use async/await... (well actually Task.Wait())Truesdale
@Iridium: All good suggests, Updated my answer to reflect one of them.Eichler
T
2

You could also use async/await pattern (using the HttpWebRequest wrapper from the Microsoft.Bcl.Async nuget package). This will also neatly handle any exceptions that occur in your background thread.

For example:

[TestMethod]
public void RunTest()
{
    bool asyncDone = false;

    // this is a dummy async task - replace it with any awaitable Task<T>
    var task = Task.Factory.StartNew(() => 
    {
        // throw here to simulate bad code
        // throw new Exception();

        // Do some stuff
        asyncDone = true;
    });

    // Use Task.Wait to pause the test thread while the background code runs.
    // Any exceptions in the task will be rethrown from here.
    task.Wait();

    // check our result was as expected
    Assert.AreEqual(true, asyncDone);
}
Truesdale answered 19/6, 2013 at 10:2 Comment(0)
G
0

it's late but I guess this would be much more readable and authentic

await Task.Delay(TimeSpan.FromSeconds(5)); 
Gigahertz answered 17/8, 2016 at 12:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.