Previously I presented a unit testing framework, QUnit-Metro, in this post I intend to provide some tips on writing code that can be easily unit tested. The techniques I will be presenting are dependency injection and using Model-View-ViewModel (MVVM) to separate user interface logic from testable business logic. The code to follow along is available on GitHub at https://github.com/jworley/QUnitMetro-Examples.

The application being tested in this example uses an API to access the trending topics on Twitter and the tweets related to each topic. The topics are presented to the user interface by way of a View Model using WinJS declarative bindings. The end product will look like this:

 API Testing – Dependency Injection

When testing APIs that consume a web service it is important to have way to decouple the API logic from the web requests. A good unit test is one that can be run today and ten years from now while still having the same result each time. A web service with live data introduces changes outside of your control, which makes checking that the resulting data is correct difficult. That is where dependency injection comes in handy, you can provide a mocked version of the web request logic and return consistent results to test against.

In strongly typed languages like C# or Java you generally need to write a class with alternate logic just for testing or use a mocking framework. But JavaScript’s dynamic nature makes mocking easy. Consider the following example:

There is no way to replace the web request logic in this example because WinJS.xhr() is used directly. If the xhr() method was loaded from a property instead it could be overwritten with a mocked replacement during the unit test. That can be achieved with the following:

In the above example a backing variable, this._xhr, and a property, this.xhr, have been added to allow unit test to provide alternate logic. In a normal use scenario the property’s get() function will be called and a copy of WinJS.xhr() will be stored in this._xhr. In unit tests, setting a value to the xhr property will override the value of this._xhr. The TrendingAPI class could be tested in the following manner:

In this test, the mock fakeXHR is looking at the URL requested by the TrendingAPI class and returning the predetermined data in a format expected by the class. Objects can be mocked in a similar manner.

ViewModels – Testing the UI

When writing the UI layer it is very easy to just call an API directly from the View logic, but doing so makes it very hard to decouple the presentation logic from the actual business logic. View and business logic should be separate because there is a possibility that the unit tests will be running in an environment that doesn’t have or doesn’t create a Metro UI (such as a Visual Studio plugin or a build server). The Model-View-ViewModel paradigm provides guidelines for separating concerns in a way that can be easily tested.

Models in MVVM are your source of data, be it a database, a web service, or a file from the file system. In the example it is the Twitter web service. Views are the code that generated the User Interface, in the case of WinJS based Metro apps that code is HTML5. ViewModels are the glue that bind Models and Views together. They provide the data from the Models in such a way so it can be data bound to by the Views. They also accept user input captured by the view and return it to the Model in a way that is appropriate. ViewModels are the best target for unit tests because they can be isolated from Models through dependency injection and the test itself can be considered another form of View.

HTML can be prepared to be used as a View by using the data-win-bind and data-win-bindsource attributes in the mark up. Below is an example of using binding attributes taken from the details view of the example project:

The text content as well as the image source are data bound using the data-win-bind attribute to properties in the ViewModel. The ViewModel for this page looks like:

A unit test for this ViewModel may look like this:

In this test a mocked SearchAPI is used to remove the uncontrollable factor of a live web service. We also remove the need to have a user interact with a UI by calling the same method a user interaction would have invoked, vm.tweetSelected(), making the unit test act as the View for this ViewModel.

Bonus – Test Harness

In the example code there are two project folders in the TrendingTweets directory, TrendingTweets and TrendingTweets.Tests. Currently there is not a way to have code included in a project that is only used in a debug or testing build and not included in a release build that would be submitted to the Windows Marketplace. So by having a second project, the tests can be isolated from the actual application so that unit tests are not accidentally included in the build submitted to the Marketplace.

Too Long – Didn’t Read

In summary, by moving logic that interfaces with outside systems to properties so they can be overridden during unit tests, one can write APIs that can be isolated from uncertainty. This produces unit test that will return the same result today and ten years from now. And by using the Model-View-ViewModel paradigm, business logic is encapsulated in ViewModel classes that can be tested in a way the decouples them from Models requiring outside data and Views that expect user input.

Useful Links

Like this post? Share it!
Free WordPress Themes, Free Android Games