Tuesday, January 15, 2019

More DI: Adding a Client-Side Cache with the Decorator Pattern

The Decorator Pattern lets us add functionality without changing existing classes. Previously, we looked at adding retry functionality and exception logging to a data reader by using decorators. This time, we'll add a client-side cache to our data reader. (For more details on how the Decorator Pattern works, see the prior article More DI: Adding Retry with the Decorator Pattern.)

Network calls, whether to a web service or a database, are often the most slowest parts of our application due latency and bandwidth limitations. To minimize these types of calls, we want to add a client-side cache to the application. This cache will provide data for a specified amount of time and then hit the real data reader when it's time to refresh the cache. This will keep the network calls to a minimum.
The Decorator Pattern lets us add functionality without changing existing classes.
This article is one of a series about dependency injection. The articles can be found here: More DI and the code is available on GitHub: https://github.com/jeremybytes/di-decorators.

Behavior Without a Cache
Before adding the cache, let's see how the application behaves. For this, we'll remove the previous decorators and go back to the base state.

Composing the Objects
Here's the composition root (from the "PeopleViewer" project, App.xaml.cs file):


The code here is much different from what is in the GitHub project. The GitHub project uses all of the decorators, and we'll look at that in a later article. To follow along with this article, you can replace the code in the "ComposeObjects" method with what we have here.

As a reminder, the "ComposeObjects" method has 3 steps. (1) It creates a ServiceReader; this is a data reader that gets data from a web service. (2) It uses the ServiceReader as a parameter to create a PeopleReaderViewModel; this view model has the presentation logic for the form. (3) It uses the view model as a parameter to create the MainWindow; this is the view that has the UI elements for the form.

Running the Application (Success)
We'll start by running the application. Since we are using the ServiceReader, we also need to start the web service. This is done from the command line with "dotnet run". (See this article for more information on how I quickly open a command prompt to the correct folder.)


Once the service is running, we'll start the application and click the "Refresh People" button.


This gives us the data from the service.

Running the Application (Failure)
Now let's stop the service and try again. To stop the service, just press "Ctrl-C" in the command window.


Then in the application, click "Clear Data" then "Refresh People". The call will fail since the service is no longer running. When the debugger stops in the code, just click "Continue" or "F5" to keep running. The application will give us the generic error message.


This shows that each time we click the "Refresh People" button, the application will make a call to the web service. This is the behavior that we'd like to change with the client-side cache.

A Client-Side Cache
To add the cache, we will decorate the data reader with caching functionality. To do this, we wrap a current data reader, add the cache, and then expose the same interface to the outside world. (For more details on using the Decorator pattern, see More DI: Adding Retry with the Decorator Pattern.)

We need to wrap and implement the IPersonReader interface (from the "Common" project, IPersonReader.cs file):


Here is the code for the CachingReader (from the "PersonReader.Decorators" project, CachingReader.cs file):


The first thing to note is that the CachingReader implements the "IPersonReader" interface, so it has the 2 methods "GetPeople" and "GetPerson".

At the top of the class, we have a field for the data reader that will be wrapped ("_wrappedReader"). This is set from an incoming constructor parameter. In addition, the constructor has a TimeSpan parameter for the duration of our client-side cache. This is also stored in a private field ("_cacheDuration").

The other 2 fields manage the cache. The "_cachedItems" field holds the data. The "_dataDateTime" is the time the data was last refreshed. This is used to see if the cache has expired.

The "GetPeople" method validates the cache and then returns the cached items.

Validating the Cache
The "ValidateCache" method ensures that we have current data. Here's the code for that:


The "if" statement checks to see if the cache is current. (We'll look at "IsCacheValid" in just a moment.) If the cache is populated and valid, then the method returns immediately, and the current cache is used.

If the cache is not populated or if it is expired, then the "GetPeople" method is called on the wrapped data reader. The items returned are put into the cache and the data date/time is updated.

If the call to "GetPeople" fails for some reason, then the CachingReader will return a default record that simply states "No Data Available". This will give us a value that we can display in the UI (and this works for this specific application). Another option would be to let the exception bubble up.

After setting the default record, a call to "InvalidateCache" ensures that the data will be refreshed with the next call. (We'll see "InvalidateCache" in just a moment.)

Checking for an Expired Cache
The "IsCacheValid" property will check to see if the cache is populated and not expired. Here's the code for that:


First, this checks to see if the cached items field is populated. If it is null, then it returns "false" immediately.

If the cache is populated, then we calculate the age of the cache by getting the difference between the data date/time and the current time. Then we check to see if the difference is less than the cache duration value.

This returns "true" if the cache is still valid.

Invalidating the Cache
We may want to invalidate the cache manually. For this, we have the "InvalidateCache" method:


This sets the data date/time to the minimum value for DateTime. This will make sure that the cache will be refreshed on the next call (unless the cache duration value is set to a *very* large value).

The implementation of this cache is fairly naive. This assumes that the data coming back from the "GetPeople" method is small enough to be stored in memory. I've used this implementation for small data sets for a while now. But it would need to be beefed up for larger data sets.

Using the Caching Decorator
Just as we did with the retry decorator and the exception logging decorator, to use the caching decorator, we snap the pieces together in a different order.

Here is the code with the CachingReader added, from the "PeopleViewer" project, App.xaml.cs file. (Reminder, this code is different from what is in the final GitHub project.)


This adds the CachingReader to the object composition. Let's walk the method from the bottom up. (1) We create a MainWindow instance. This needs an "IPeopleViewModel" as a parameter. (2) So above that, we create a PeopleReaderViewModel to pass to the MainWindow. The PeopleReaderViewModel needs an "IPersonReader". (3) So above that we create the CachingReader to pass to the view model. The CachingReader has 2 parameters: an "IPersonReader" to wrap and a TimeSpan for the duration. (4) So above that we create a ServiceReader to get data from a web service and also create a TimeSpan of 10 seconds.

Running the Application (Success)
So let's see the cache in action. First, start the web service like we did above:


Then run the application and click "Refresh People". This gives us the expected data:


Now we'll check the cache. Keep the application running for the next steps.

Using the Cache
With the application still running, stop the web service using "Ctrl-C" as we did above.


Now click the "Clear Data" and "Refresh People" buttons in the application.


We still have data!

Now if we continue to click "Clear Data" and "Refresh Data", the cache will eventually expire. After the cache is expired, the CachingReader tries to get data from the service (which is no longer running). That call fails, so the CachingReader returns the default record:


This shows the client-side cache in action. Even though the web service is no longer running, we can still get data from the client-side cache until the data gets too old.

Note: if the cache is expiring too quickly to see these results, just set the "duration" to a larger value in the "ComposeObjects" method above.

This code works with the CSVReader and SQLReader as well. I use the ServiceReader here since it is the easiest to demonstrate by stopping the service.

Unit Testing the Caching Decorator
Just like with the other decorators, we want to unit test the caching decorator. The process is very similar to the previous tests, so we'll go pretty quickly through this. For more details, see More DI: Unit Testing Async Methods which shows the tests for the retry decorator.

These tests are in the "PersonReader.Decorator.Tests" project, CachingReaderTests.cs file.

Testing That the Cache is *Not* Used
Our first test will check to make sure that the cache is not used on the first call to the "GetPeople" method. Here's the code for that:


Just like with our other decorators, we want to use a fake data reader for testing. In this case, we have a "CountedReader" that keeps track of how many times the "GetPeople" method is called.

Let's look at the fake reader and then come back to this test.

Counting Method Calls
The fake data reader is in the same testing project, CountedReader.cs file:


The CountedReader implements "IPersonReader" and so has the "GetPeople" and "GetPerson" methods. The class has a public property to keep track of how many times the methods are called. This property is public, so it is available to the tests.

The "GetPeople" method increments the call count and then returns an empty collection of Person objects. The "GetPerson" method does something similar. We don't need any fake data here, so the empty results are fine.

Back to the Test
Let's go back to the first test.


Here, we create a CountedReader and a duration (a TimeSpan of 1 second). Then we use these values to create the CachingReader.

Next, we call the "GetPeople" method.

Finally, we assert that the CallCount on the CountedReader is 1. This lets us know that the wrapped data reader was called one time for the test. And this is exactly what we expect.

Testing the Cache
The next test will see if the cache is working. Here's the code:


The name of the test tells how we're going to verify that the cache is being used. When the "GetPeople" method is called 2 times, we expect that the wrapped reader is only called 1 time.

The setup for this test is the same as the prior one. We create a caching reader with a duration of 1 second.

For the action, we call "GetPeople" twice. We expect that the second call should use the cache.

Then we check to see how many times the CountedReader is called. We expect that it will be called the first time but not the second time. So, the CallCount should be 1.

Verifying an Expired Cache
For the last test, we want to make sure that the reader still works after the cache has expired.


This test has the same setup: we create a CachingReader with a 1 second cache.

In the action section, we call the "GetPeople" method. This should populate the cache.

Then we wait for 2 seconds. (For more information on how "Task.Delay" works, take a look at the article for the Retry Decorator.)

After waiting 2 seconds, we call the "GetPeople" method again. The cache should be expired, so we expect that the wrapped data reader will be called to refresh the cache.

The result is that we expect the CountedReader to be called twice. And that is what we check with the assertion.

Note: This test takes at least 2 seconds to complete. This is a slooow test, so I've marked it with the "Slow" category. It is possible to create shorter durations for the cache and for the delay, but if we cut things too fine, then we may end up with timing issues in the test.

Test Results
When we run the tests, they all pass:


And we can see that the third tests did take 2 seconds to complete.

Dependency Injection and the Decorator Pattern
Dependency Injection and the Decorator Pattern work really well together. We can easily add retry functionality, exception logging, or a client-side cache to our application. We just need to take the loosely coupled pieces and snap them together in a different order.

We did *not* need to change the view (MainWindow). We did *not* need to change the view model (PeopleReaderViewModel). We did *not* need to change the existing data reader (ServiceReader). We just snap our pieces together in a different order.

It seems like I'm repeating this quite a bit. And that's because it is so important. Decorators let us add functionality in pieces. These atomic units are easier to test, and we can compose them in whatever order we like.

Since our decorators wrap "IPersonReader" objects, and our decorators are "IPersonReader" objects, we can have our decorators wrap other decorators. This will stack the functionality so that we get all of the features that we want. That's what we'll look at next time. So be sure to check back.

Happy Coding!

Monday, January 14, 2019

More DI: Adding Exception Logging with the Decorator Pattern

The Decorator Pattern lets us add functionality without changing existing classes. Previously, we looked at adding retry functionality to a data reader by using a decorator. This time, we'll add exception logging to our data reader. (For more details on how the Decorator Pattern works, see the prior article More DI: Adding Retry with the Decorator Pattern.)

In this application, if the data reader fails for some reason, we want to make sure the exception gets logged. We'll log to the file system to keep things simple, but we can use whatever logging system we'd like.
The Decorator Pattern lets us add functionality without changing existing classes.
This article is one of a series about dependency injection. The articles can be found here: More DI and the code is available on GitHub: https://github.com/jeremybytes/di-decorators.

Behavior Without Logging
Before adding exception logging, let's look at the existing behavior of the application. For this, we'll remove the previous decorators and go back to our original object composition.

Composing the Objects
Here's the composition root for the application in the "PeopleViewer" project, App.xaml.cs file.


The code here is much different from what is in the GitHub project. The GitHub project uses all of the decorators. We'll look at that in a later article. To follow along with this article, you can replace the code in the "ComposeObjects" method with what we have here.

As a reminder, the "ComposeObjects" method has 3 steps. (1) It creates a ServiceReader; this is a data reader that gets data from a web service. (2) It uses the ServiceReader as a parameter to create a PeopleReaderViewModel; this view model has the presentation logic for the form. (3) It uses the view model as a parameter to create the MainWindow; this is the view that has the UI elements for the form.

Running the Application (Failure)
Since the application is configured to use the web service, let's see what happens when we run the application *without* starting the service.

If we run and then click the "Refresh People" button, we get the following result. (Note: if you are running in the debugger, the debugger will stop when an exception is thrown. Just press "Continue" or "F5" to keep going.)


The application gives us a pop-up message that comes from a global exception handler. The message does not tell us exactly what happened. It is generally a bad idea to show raw exception messages to our users. Instead, we should give them a message that is more appropriate.

The problem right now is that we are not logging the exception.

Logging the Exception
Since the exceptions come from the data reader, we'll use a decorator to intercept the exceptions so we can log them. For this, we'll create an ExceptionLoggingReader.

As a reminder, to create a decorator, we wrap an existing implementation of an interface and add our own behavior. Then we expose the same interface so that it looks like any other data reader.

Here is the IPersonReader interface (from the "Common" project, IPersonReader.cs file):


This is the interface that we want to wrap as well as implement.

Here is the code for our exception logging decorator (from the "PersonReader.Decorators" project, ExceptionLoggingReader.cs file):


The first thing to notice is that this class implements "IPersonReader", so it has the methods "GetPeople" and "GetPerson". This lets us plug this into our application just like any other data reader.

The constructor takes 2 parameters. The first is an "IPersonReader"; this is the data reader that we're wrapping, and we store it in a private field.

The second constructor parameter is an "ILogger". This will handle the logging calls, and we store this in a private fields as well. We'll look at the details of the logger in just a bit.

In the "GetPeople" method, we call the "GetPeople" method on the wrapped data reader. If the wrapped reader returns successfully, we pass along that value.

If the wrapped reader throws an exception, we log the exception and then rethrow it. This particular class is not designed to handle the exception; the exception will continue to bubble up the call stack until it hits our global exception handler. But we are intercepting the exception so that we can log the details.

The Logger Interface
The ILogger interface has 2 methods (from the "Logging" project, ILogger.cs file):


Our logger implementations need to have methods to log an exception and to log a message.

As a side note, this is in a "Logging" project separate from the data reader and decorator projects. The idea is that the logger can be used by other parts of the application as well, so we have it is a location that can be easily shared.

A File Logger
For this application we will log to a file. So we have a FileLogger class that implements the ILogger interface. Here's the code (from the "Logger" project, FileLogger.cs file).


Since this implements that "ILogger" interface, it has both the "LogMessage" and "LogException" methods.

At the top of the class, we have a field to represent the log file. This is a hard-coded location: the application folder. So this will be in the same folder as the executable. The file name is also hard-coded: ExceptionLog.txt. We can parameterize these values if we need to, but this will get things working.

The "LogException" method uses a StreamWriter to write to a file. The "true" parameter used in the constructor specifies that we want to append to the file (it will also create the file if it does not exist).

Then we generate the message and write it to the log file.

Note: this is a naive implementation of a file logger. There are several things to take into consideration before using something like this in production. Primarily, if the logging fails (such as if it doesn't have permissions to write the file), then an unhandled exception will be thrown. Logging is a complex topic, and we should seriously consider using a third-party logger for our applications. Other people have solved this problem already. We should take advantage of that.

Using the Exception Logging Decorator
Just as we did with the retry decorator, to use the exception logger we just need to snap our pieces together in a different order.

Here is the code with the ExceptionLoggingReader added (in the "PeopleViewer" project, App.xaml.cs file -- again this code is different from what is in the GitHub project):


Let's walk this code from the bottom up. The last line creates a MainWindow class (the view). The view constructor needs an IPeopleViewModel. So above that, we create a view model (PeopleReaderViewModel) to pass to the view constructor.

The PeopleReaderViewModel constructor needs an IPersonReader. Above that, we create an ExceptionLoggingReader to pass to the view model constructor.

The ExceptionLoggingReader constructor needs both an IPersonReader (the wrapped data reader) as well as an ILogger. So above that, we create a ServiceReader and a FileLogger.

To use the exception logging decorator, we simply snap our pieces together in a different order.

Running the Application (Failure)
To see the functionality in action, we will run the PeopleViewer application just like we did above.

When we click the "Refresh People" button (and then click "Continue" when the debugger stops), we get the error popup:


Just like before, we get the generic failure message. But now we have a log file to look at.

Viewing the Log
As we saw above, the FileLogger uses the same path as the executable. We can navigate to that location to see the log file.

Note: the way I get there is to right-click on the "PeopleViewer" project in the Visual Studio Solution Explorer, then select "Open Folder in File Explorer". From there, I just click into the "bin" folder, then the "Debug" folder.

In that folder we find the "ExceptionLog.txt" file. Here is the content:


This shows that we got an "HttpRequestException" since the web service was not running. We can also see the stack trace showing that the exception was thrown in the "ServiceReader.GetPeople" method which was called by the "ExceptionLoggingReader.GetPeople" method.

The log file has all of the details of the exception. So we can use this to see what is really happening in our system.

Unit Testing the Exception Logging Decorator
Before wrapping things up, let's look at how we can unit test the ExceptingLoggingReader class. This is fairly similar to how we tested the RetryReader.

Testing the Happy Path
For the first test, we will check to make sure that nothing gets logged if the call returns successfully. Let's start by creating the objects. (The tests are in the "PersonReader.Decorators.Tests" project, ExceptionLoggingReaderTests.cs file.)


For this test, we can use the same BrokenReader fake class that we used for the RetryReader tests (see the previous article for details). When we pass "0" as a parameter, the calls will complete successfully (i.e., not broken).

A Fake Logger
When we create the ExceptionLoggingReader, we pass in the fake reader, but we also need an ILogger. We don't want to use the FileLogger because that would rely on the file system. Instead, I created an in-memory logger that can be used for testing.

This code is in the StringLogger.cs file (still in the same test project):


This class implements the "ILogger" interface, so it has the "LogException" and "LogMessage" methods.

At the top of the class is a public "Log" property. This is just a string to hold log messages. The methods append to this property. Since the property is public, we can inspect the value in our tests.

Note: these are asynchronous methods and must return a Task. An easy way to do this is with the "Task.CompletedTask" static property. This saves us from needing to create a new Task ourselves.

Finishing the First Test
With the fake logger in place, we can finish up the code for the first test (in the ExceptionLoggingReaderTests.cs file):


A new instance of the StringLogger is created for this test, so we know that the "Log" property will be empty when we start.

After calling the "GetPeople" method, we expect that the "Log" property will still be empty. And that is exactly what this test does.

Note: for more information on unit testing async methods, take a look at the previous article More DI: Unit Testing Async Methods.

Testing the Log
Now that we've tested that the ExceptionLoggingReader does nothing if there is no exception, let's test what happens if there *is* an exception.

Here is the test:


This test is set up a little bit different. First, notice that the "BrokenReader" constructor is passed a value of "1". This means that when we call the "GetPeople" method, we expect that it will be broken the first time. (And since we're only calling it once, this is all we need.)

The "expectedMessage" is set to an invalid value. The correct value should be set in the "catch" block as we'll see below. But if something goes wrong, we don't want our test to pass accidentally.

In the "try" block, we expect that the call to "GetPeople" will throw an exception. Because of this, I've added an "Assert.Fail" as a sanity check. If the "GetPeople" method does *not* throw an exception, then this block will get hit and the test will fail.

In the "catch" block, we look at the exception that is thrown. Since the ExceptionLoggingReader does not handle the exception, we will see it here. We can look at the exception message and use it as our "expectedMessage".

The assertion checks to see if the message from the exception we just caught is somewhere in the Log. If it is in the Log, then that means the exception was logged as expected.

Test Results
When we run these tests, we can see that they both pass:



Dependency Injection and the Decorator Pattern
Dependency Injection and the Decorator Pattern work really well together. Just like we saw with the RetryReader, we can use the ExceptionLoggingReader to injection functionality into our application. We just need to take our loosely coupled pieces and snap them together in a different order.

And since the decorator wraps an IPersonReader, it will work with any of our data readers (the ServiceReader, CSVReader, and SQLReader). And we did not need to modify any of our existing objects.

We did *not* need to change the view (MainWindow). We did *not* need to change the view model (PeopleReaderViewModel). We did *not* need to change the existing data reader (ServiceReader). We just snap our pieces together in a different order.

We can also stack our decorators so that decorators end up adding functionality to other decorators. This way we can add functionality a bit at a time, which makes it easier to test and also to combine in different ways depending on what our application needs. We will look at this in a future article.

Coming up, we will look at a CachingReader. This will add a client-side cache to the application. And as with our other decorators, we can get the functionality without modifying our existing objects. So be sure to stay tuned for more.

Happy Coding!

Thursday, January 10, 2019

More DI: Unit Testing Async Methods

I'm a big believer in unit tests. I've seen how unit tests make me a faster developer (check out my unit testing materials for more information). There are a few quirks when we're testing asynchronous methods.

Last time, we looked at a decorator class that adds retry functionality (More DI: Adding Retry with the Decorator Pattern). This time, we'll write some unit tests to make sure the class behaves as expected.
There are a few quirks when we're testing asynchronous methods.
This article is one of a series about dependency injection. The articles can be found here: More DI and the code is available on GitHub: https://github.com/jeremybytes/di-decorators.

The Unit Under Test
We're testing the retry functionality in our decorator. This is in the "PersonReader.Decorators" project, RetryReader.cs file, specifically the "GetPeople" method.


This method should try to call a method (up to) 3 times. If the call is successful, it returns the results. If the call throws an exception, then it waits 3 seconds and tries again. For details on this method, see the previous article.

This class wraps another data reader (such as a ServiceReader) that provides the actual data. In our tests, we will create a fake object to handle this functionality.

For testing, we want to test 3 scenarios. These are the same 3 scenarios that we checked by manually running the application in the previous article. (1) Success on the first try; (2) Failure on the first try with success on a subsequent try; (3) Failure after exhausting the retry attempts.

Let's head over to the tests.

Unit Testing the Retry Reader
We already have a test project set up: PeopleReader.Decorators.Tests. The completed tests are in the RetryReaderTests.cs file. Let's walk through creating the first test.

The first test will ensure that the Retry Reader returns data when there are no problems with the wrapped data reader -- the "happy path". Here's a start to that test:


First, about the name: I use a 3-part naming system. The first part is the unit under test ("GetPeople"). The second part is the state that we're testing (that the wrapped reader is "NotBroken"). The third part is the expected outcome (that the method "ReturnsPeople" successfully).

We start by creating an instance of the RetryReader class that we're testing. The constructor needs a parameter which is an IPersonReader (the "real" data reader that we're wrapping). Since we don't want to use a real data reader, we'll create a fake one.

A Fake Reader
The fake reader will need to implement the "IPersonReader" interface (from the "Common" project, IPersonReader.cs file).


The interface has 2 methods that both return Task, so we're dealing with asynchronous methods here.

For the fake reader, we want to be able to control whether it returns successfully or throws an exception. So we'll create a class that has a "brokenCount" value. This value determines how many times the method should fail before it succeeds.

Here's the code for the BrokenReader (in the "PersonReader.Decorator.Tests" project, BrokenReader.cs file):


The first thing to note about this class is that it implements the "IPersonReader" interface, so it has the 2 members "GetPeople" and "GetPerson".

At the top of the class, we have some fields to help us. The "brokenCount" holds the number of times the class should fail before it returns successfully. If the "brokenCount" is 0, then all calls will be successful. If the "brokenCount" is 1, then the first call will fail, but the second call will succeed. This value is set by a constructor parameter.

The "retryCount" is used internally to keep track of how many calls have been made so far.

The "testPeople" field is our test data that is returned when the call is successful.

The "GetPeople" method returns a Task<IEnumerable<Person>>. Before we look at the final code, let's walk through a few things we might try.

First, we might try to write the method as if we didn't have to worry about Task:


The "if" block checks to see if we've hit the threshold. If the retry count is less than the broken count, the we want the call to fail by throwing an exception. The retry count is also incremented so the value will be different with the next call.

For the "return" of this method, we try to return "testPeople". But this fails because "testPeople" is a List<Person>. List<Person> implements IEnumerable<Person>, so those types are compatible, but we really need a Task<IEnumerable<Person>>.

There are a couple of approaches. One option is to wrap the "testPeople" in a Task:


This option works, but I'm not a big fan of the syntax. It's a bit difficult to read, particularly for people who aren't well-versed in using Task directly.

The next option feels like a bit of a cheat, but I like it quite a bit better:


In this code, we "await Task.Delay(1)". This will pause operation for 1 millisecond. But the more important thing is that when we "await" something in a method, the whole method becomes asynchronous (we also need to mark the method with the "async" modifier).

When we changed the interface to async methods (More DI: Async Interfaces), we saw that when we "await" something in a method, the return value is automatically wrapped in a Task for us.

In this method, that means we can "return testPeople", and it will automatically be wrapped in a Task.

Using "await Task.Delay(1)" is not something I would use in production code -- it is an artificial delay after all. But for unit tests, I'm willing to take the slight performance hit so that the tests are more easily approachable.

Update 1/10/2019: 
As Graham King points out in the comments, another option is to use Task.FromResult() to create a completed task with the desired result. This is a good option (that I completely forgot about), so let's take a look.

Here's the "GetPeople" method using Task.FromResult:


The idea is that "Task.FromResult" will create a completed Task that has "testPeople" as the result. Unfortunately, the compiler is not happy here. The error tells us that we have a type mismatch between the expected result (IEnumerable<Person>) and the actual result (List<Person>, which is the "testPeople" type). Even though List<T> implements IEnumerable<T> and "testPeople" is an IEnumerable<Person>, Task needs us to be more specific.

To fix the code, we can add a generic type to the "FromResult" call:


Or we can add "AsEnumerable" to the "testPeople" parameter:


But probably the best option is to change the type of the "testPeople" field from "List<Person>" to "IEnumerable<Person>":


Then we don't have to worry about the type coercion.


This feels a lot better, and this is what you'll see in the final code for this class (BrokenReader.cs file). Thanks, Graham!

Using the Fake Broken Reader
Now, we'll flip back to our tests and create an instance of the "BrokenReader" and pass it into the RetryReader constructor (in the RetryReaderTests.cs file).


After creating the RetryReader instance, we need to call the "GetPeople" method. It's really tempting to use the code here because this is the way I normally code up the "action" part of the unit test.

The danger is that it's easy to assume that "result" is the Person data. But if we inspect the variable type, we see that it is actually a Task:


A clue to how we should actually write the code is in the "Usage" section of the popup. Instead of simply calling the "GetPeople" method, we should "await" it. This will give us the "IEnumerable<Person>" that we can use for the assertion.

Here's the updated test (which is the final code in the RetryReaderTests.cs file):


Update 1/15/2019: A "retryDelay" parameter has been added to the RetryReader constructor. This lets us control the delay between retries. The updated code will be shown below.

As noted above, we now "await" the GetPeople method. Because we are using "await", we must also mark the test method with the "async" modifier.

Another thing that we need to change is the return type of the test. Previously, it was "void", but when we return "void", we lose all visibility to the Task, including any exceptions that might be thrown. So we need to change the return type to "Task". The good news is that if we forget to do this, the testing framework will remind us when we try to run the test.

In the assertion, we are checking to see that the "result" coming back from the GetPeople method is not null. We could also do a more specific check for the 2 items in the data, but we're actually more concerned that this does not throw an exception.

The last thing I did was change the name of the test to have "Broken0" in the name. I'm not sure I like this name, but it goes along with the next 2 tests. I'm thinking about changing the names of all of the tests, so they may be a little different in the final code.

Another Test
The next test will see if the retry functionality works. We want the fake reader to fail on the first call, but succeed on the second one. Here's what that tests looks like (from the RetryReaderTests.cs file):


This test is almost identical to the previous test. The difference is that the "BrokenReader" class is passed value of 1, meaning we want the first call to fail, but the second call to succeed.

The rest of the test is the same. We expect that we will get data back (eventually) and that we will not get an exception.

The caveat is that this test is slooow (and that's why I gave it a category). This test takes at least 3 seconds to complete since the RetryReader waits 3 seconds before retrying the method call.

It's not great, but it's an accurate test of the functionality. The category attribute gives us a chance to filter these tests in the test runner so that we're not constantly running them with the rest of the test suite.

Update 1/15/2019: A "retryDelay" parameter has been added to the RetryReader. This lets us set the value to "0" for unit testing, so we get immediate retries, and the tests no longer run slowly. Here is the updated code for this test (similar updates were made to the other tests and are available on GitHub.)


This test sets the retry delay to "0", so this is no longer a slow test.

Testing for Failure
The last test is to see what happens when all of the retries fail. In this case, we expect that the original exception from the wrapped data reader will be thrown.

This test looks a bit different from the other 2 (this is in the same file):


This time, we give the "BrokenReader" a value of 3. So it should fail on 3 calls before returning successfully. Since our RetryReader only tries 3 times, we expect that the overall call will fail.

The "try/catch" block is set up to ensure that we get the expected exception when the "GetPeople" method is called. For more information on this pattern (and a couple others), take a look at Testing for Exceptions with NUnit. I opted to go with the option that is not test framework specific.

In the "try" block, if there is no exception thrown, then we hit the "Assert.Fail" call. This will fail the test.

If an exception is thrown, then we hit the "catch" block. The "Assert.Pass" is not strictly necessary; if we leave the catch block empty, this test will still pass. But I like to add the "Pass" to make it more clear that an exception is what we expect to happen here.

This is also a sloooooow test (it takes at least 6 seconds). So we've marked it with the "Slow" category as well.

Update 1/15/2019: A "retryDelay" parameter has been added to the RetryReader. This value can be set to "0" in the unit tests so that they are no longer slow.



Test Results
All of these tests pass:


This also shows the timings, and we can see that the slow tests do take 3 seconds and 6 seconds, respectively.

Update 1/15/2019: With the "retryDelay" values specified above, the tests run much more quickly:



Async Unit Tests
Overall, having async methods in unit tests is not that much different from having async methods in our production code. We can use async/await with our test frameworks (just remember to return "Task" instead of "void" on the test methods).

When we fake up data, we often don't need to make async calls to a data store. As a cheat that keeps the code readable, we can "await Task.Delay(1)" in our fake objects. This will make the method asynchronous and automatically wrap the return value in a Task.

There's a lot more to explore with this project, including the other decorators, configuration, and how .NET Standard and .NET Framework projects interact. So stay tuned for more.

Happy Coding!