This is the second part of Stefan Lieser’s article appearing in DotNetPro. Stefan was generous to translate the article and post it here. (Read the first part)
Stefan Lieser works as a consultant, trainer and author. He is interested in agile methods and looking for improvement every day. Together with Ralf Westphal he started the Clean Code Developer initiative (see http://clean-code-developer.de) which gains lots of interest in both the .NET and Java software developer community. Furthermore he was one of the organizers of the .NET Open Space in Leipzig, Germany.
How do you test asynchronous code? Part II
It‘s all faked
We can now use the additional constructor to inject a mocked WebClient instance so we don’t start a real download in the automated tests. To be able to inject a mock in tests and “the real thing” in production we need a common interface for both. Unfortunately, Microsoft decided that WebClient would not implement any interface. It only inherits from Component which is of no use in this context. I see two ways out of this dilemma: the first is to define our own interface. That means implementing a wrapper class around WebClient that implements our interface and uses WebClient internally to do its job. Writing such wrappers is not only dull but also more difficult: class DownloadDataCompletedEventArgs has only internal constructors so we can’t use it in our wrapper. Going the wrapper route means not only wrapping WebClient but implementing our own EventArgs class. What those wrappers may look like is shown by the following code snippets:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<span class="kwrd">public</span> <span class="kwrd">interface</span> IWebClient { <span class="kwrd">event</span> EventHandler<DownloadDataCompletedEventArgsWrapper> DownloadDataCompleted; <span class="kwrd">void</span> DownloadDataAsync(Uri address); } <span class="kwrd">public</span> <span class="kwrd">class</span> WebClientWrapper : IWebClient { <span class="kwrd">private</span> <span class="kwrd">readonly</span> WebClient webClient = <span class="kwrd">new</span> WebClient(); <span class="kwrd">public</span> WebClientWrapper() { webClient.DownloadDataCompleted += ProcessDownloadDataCompleted; } <span class="kwrd">private</span> <span class="kwrd">void</span> ProcessDownloadDataCompleted(<span class="kwrd">object</span> sender, DownloadDataCompletedEventArgs e) { DownloadDataCompleted(sender, <span class="kwrd">new</span> DownloadDataCompletedEventArgsWrapper(e)); } <span class="kwrd">public</span> <span class="kwrd">event</span> EventHandler<DownloadDataCompletedEventArgsWrapper> DownloadDataCompleted = <span class="kwrd">delegate</span> { }; <span class="kwrd">public</span> <span class="kwrd">void</span> DownloadDataAsync(Uri address) { webClient.DownloadDataAsync(address); } } |
The real WebClient class is used inside the wrapper to do the work. The methods from the wrapper refer to the WebClient instance to do their work. If you don’t want to write those wrappers you will need a powerful testing tool.
Mocking with the Profiler API
Instead of writing wrappers for infrastructure classes from the .NET Framework we can use powerful technologies like the Profiler API to be able to mock the objects. By using the Profiler API one can intercept nearly every call. That’s what the popular Typemock Isolator uses internally.
I’m still a fan of injecting dependencies as mentioned above with an internal constructor to make the tests more expressive. Typemock Isolator, however, can intercept even the call to new inside of the method. Using Typemock Isolator the automated test looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
[TestFixture] <span class="kwrd">public</span> <span class="kwrd">class</span> ArticleRepositoryTests { <span class="kwrd">private</span> ArticleRepository sut; <span class="kwrd">private</span> WebClient webClient; <span class="kwrd">private</span> IArticleReader articleReader; [SetUp] <span class="kwrd">public</span> <span class="kwrd">void</span> Setup() { webClient = Isolate.Fake.Instance<WebClient>; Isolate.WhenCalled(() => webClient.DownloadDataCompleted += <span class="kwrd">null</span>).CallOriginal(); articleReader = Isolate.Fake.Instance<IArticleReader>(); sut = <span class="kwrd">new</span> ArticleRepository(() => webClient, articleReader); } [Test, Isolated] <span class="kwrd">public</span> <span class="kwrd">void</span> WebClient_Data_is_handled_by_Reader() { var e = Isolate.Fake.Instance<DownloadDataCompletedEventArgs>(); var count = 0; sut.OnArticleLoaded += <span class="kwrd">delegate</span> { count++; }; sut.LoadArticles(); Isolate.Invoke.Event(() => webClient.DownloadDataCompleted += <span class="kwrd">null</span>, <span class="kwrd">null</span>, e); Assert.That(count, Is.EqualTo(1)); } } |
In the example we created a WebClient instance using the following snippet:
webClient = Isolate.Fake.Instance<WebClient>;
Because we used Isolator to instantiate the object we were able to modify its behavior in the test. Isolator intercepted the call to DownloadDataAsync. The download didn’t really start and we isolated our repository from the real resource.
In order to give the repository some test data we had to invoke the DownloadDataCompleted event. And doing this meant that we needed an instance of the event argument. As mentioned above this had an internal constructor but with Isolator to the rescue we solved this with the following snippet:
var e = Isolate.Fake.Instance<DownloadDataCompletedEventArgs>();
After creating the event argument we told Isolator to raise the event:
Isolate.Invoke.Event(() => webClient.DownloadDataCompleted += null, null, e);
In the end this test shows that the data that is transmitted by the WebClient to our repository is received by the object that handles the data.
What about asynchronicity?
But now back to the problem of testing the asynchronous call. As shown in the code example the ArticleRepository uses WebClient to download the XML data and creates article objects from that data. The repository itself should not map from XML to article objects. If it did, the tests would be much more difficult because the data would have to go through the WebClient mock.
In order to separate the mapping concern we could inject another object into the repository. The responsibility of the repository would then be to download the data by using the WebClient and hand it over to a mapper to create article objects from it. So let’s create an interface for that:
1 2 3 4 |
<span class="kwrd">public</span> <span class="kwrd">interface</span> IArticleReader { IEnumerable<Article> Read(<span class="kwrd">byte</span>[] articleXml); } |
The IArticleReader maps from a byte-array containing the XML to an enumerable of articles. That’s pretty easy to test.
The only thing we have to test for the repository is if it hands the downloaded data to the IArticleReader object:
1 2 3 4 5 6 7 8 9 10 11 |
[Test, Isolated] <span class="kwrd">public</span> <span class="kwrd">void</span> WebClient_Data_is_handed_to_article_reader() { var e = Isolate.Fake.Instance<DownloadDataCompletedEventArgs>(); var downloadedData = <span class="kwrd">new</span> <span class="kwrd">byte</span>[0]; Isolate.WhenCalled(() => e.Result).WillReturn(downloadedData); sut.LoadArticles(); Isolate.Invoke.Event(() => webClient.DownloadDataCompleted += <span class="kwrd">null</span>, <span class="kwrd">null</span>, e); Isolate.Verify.WasCalledWithExactArguments(() => articleReader.Read(downloadedData)); } |
Where have the async problems gone? They have disappeared because we separated the concerns of the repository. We now only need an integration test that verifies we can really download the data from a real server and deliver the correct article objects. And of course that integration test needs to handle the asynchronous method call
The problem with the integration test is that after calling LoadArticles the method immediately returns, forcing us to wait for the result. But how long should we wait, ten seconds? That may be enough on a developer machine but not on the build server. Waiting even longer means slowing down the tests so using Thread.Sleep is not a good idea.
I recommend using an EventWaitHandle which makes it possible to wait for the event or a timeout. The test looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[Test] <span class="kwrd">public</span> <span class="kwrd">void</span> Articles_get_loaded() { var waitHandle = <span class="kwrd">new</span> EventWaitHandle(<span class="kwrd">false</span>, EventResetMode.ManualReset); var count = 0; sut.OnArticleLoaded += <span class="kwrd">delegate</span>(IEnumerable<Article> articles) { count++; Assert.That(articles, Is.Not.Null); waitHandle.Set(); }; sut.LoadArticles(); var wasSignalled = waitHandle.WaitOne(5000); Assert.That(wasSignalled, Is.True, <span class="str">"A timeout occurred"</span>); Assert.That(count, Is.EqualTo(1)); } |
Conclusion
Testing is easy if the basic principles for inner software quality are followed. In this example the Separation of Concerns leads to code that is easy to test. Of course, principles need to be followed in the context of a value system. If the only value were correctness than writing the wrapper classes would not be a problem. There is, however, the value of production efficiency. It’s more efficient to use tools like Typemock Isolator to overcome technical limitations. You may decide not to inject every dependency and that’s fine, as long as it’s good for efficiency and does no harm to correctness and evolvability.