Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Isolating Dependencies in Tests Using Mocks and Stubs : Page 2

Unit tests are not "unit" tests if they test things other than the System Under Test (SUT).


Dealing with SUT Dependencies

The VideosListPresentationModel class has two dependencies: an object that implements IVideosListView, and an object that implements IVideosRepository. When writing tests for your SUT, you need to isolate those dependencies.

The first set of tests check to make sure the presentation model's initial state has been properly set. There are three things to check for:

  • The object's View property should have a reference to the view object passed to its constructor.
  • The Model property on the given view object should have a reference to the presentation model itself.
  • No filter should be set by default, which you can check by inspecting the CurrentVideoTypeFilter property.

One of your first tasks when writing these tests is to instantiate the VideosListPresentationModel class. The snippet below shows the signature of the class's constructor:

public VideosListPresentationModel( IVideosListView view, IVideoRepository repository) { ... }

The constructor's signature tells you that the class expects two dependencies to be injected into it: a view and a repository, of types IVideosListView and IVideoRepository, respectively. The parameters are typed as interfaces, so you can use any object that implements those interfaces. Besides abiding by the Dependency Inversion Principle (by making the class depend on abstractions, instead of concretions), this approach also helps with testability, since you don't need to pass in the real implementation of those dependencies when writing the unit tests.

You saw a class that implements IVideosListView earlier in this article: the VideosListView class. You may be thinking "Ah, so I could instantiate that class and pass it into the presentation model, couldn't I?"

Yes, you could, but what if that class had some bugs that could interfere with your tests on the presentation model class? That'd be bad; unit tests shouldn't fail because of defects of a class's dependencies.

"But that VideosListView class seemed so simple; what could possibly go wrong?" The answer is: quite a few things.

For instance, there could be some problem with the XAML that prevents the class from being instantiated (a simple resource dictionary that can't be found during run time would do it). Or maybe the window uses a third-party control for which you only have a trial version; a nag screen pops up whenever the window gets instantiated. You certainly don't want a nag screen showing up when you run your tests.

Author's Note: Of course, there's a lot more that could go wrong, but these examples sprang to mind first, because they happened to me recently!

What about that "repository" parameter? In this scenario, that object's responsibility is to provide data to the presentation model (some people may jump at me saying that I should use a domain service instead of a repository, but bear with me; this is just a simple example).

The presentation model has no business knowing how the repository obtains the data (it could be using a database, an Object-Relation Mapper framework, or reading the data from an XML file somewhere). Further, anything that goes wrong with the repository should not interfere with your tests on the presentation model.

Think about it: there are a lot of things that can go wrong when you try to retrieve data from a database, for instance. Besides, even if the test could get to the database, you'd be violating two best practices for unit tests: you'd be accessing an external resource, and you'd be causing the test to run slowly (because connecting to a database and retrieving data is a slow process by nature).

By now you've established you can't use the VideosListView class. So what do you do? Remember the constructor expects any object that implements the IVideosListView interface? You could pass in a fake object; a stub that looks like the real one, but doesn't really do anything. The same solution applies to the repository.

Using Static Fake Objects

A static stub for IVideosListView is nothing more than a class statically defined by the developer. Such a class has just enough implementation code to satisfy the compiler:

public class StubView : IVideosListView { public IVideosListPresentationModel Model { get; set; } public void Show() {} public event EventHandler <ShowVideoDetailsRequestedEventArgs> ShowVideoDetailsRequested; public event EventHandler<VideoListRequestedEventArgs> VideoListRequested; }

Notice in the preceding code that the Model property is an auto-implemented property (therefore providing the minimal behavior for a property), the Show method doesn't have any implementation code, and the two events, ShowVideoDetailsRequested and VideoListRequested, are declared but there's nothing handling them.

A static stub is nothing more than a class statically defined by the developer.

The code below shows a similar stub class implementation for IVideoRepository:

public class StubRepository : IVideoRepository { public IEnumerable<Video> GetAllVideos() { return null; } public IEnumerable<Video> GetAllVideosOfType(VideoType videoTypeFilter) { return null; } }

At this point you're ready to start writing your tests. Remember, the first tests you're going to write check the expectations on the presentation model for its initial state. You start the test class as follows:

[TestClass]public class VideosListPresentationModel_InitialState_Tests { private static VideosListPresentationModel PM; private static StubView View; private static StubRepository Repository; [ClassInitialize()] public static void FixtureSetup(TestContext tc) { View = new StubView(); Repository = new StubRepository(); PM = new VideosListPresentationModel(View, Repository); } }

Author's Note: Notice that I use a FixtureSetup method (which runs before all tests within the test class are run) to instantiate both the stub classes as well as the presentation model.

Next, write the tests according to the expectations you had outlined earlier (the name of the test methods should remind you of what those expectations are):

[TestMethod] public void Should_set_view_to_View_property() {Assert.AreEqual(View, PM.View); } [TestMethod] public void Should_set_model_for_given_view() {Assert.AreEqual(PM, View.Model);} [TestMethod] public void Should_default_to_NoFilter() {Assert.AreEqual("(No Filter)", PM.CurrentVideoTypeFilter);}

These tests all fail because the behaviors aren't implemented on the presentation model yet (which is good Test-Driven Development practice). Go ahead and implement the constructor on the presentation model class:

public VideosListPresentationModel( IVideosListView view, IVideoRepository repository) { m_Repository = repository; View = view; View.Model = this; CurrentVideoTypeFilter = "(No Filter)"; }

You've implemented a few unit tests for the VideosListPresentationModel class, and you're testing only the class's implementation, without having to worry about the real implementation of its dependencies (in this case, the view and the repository). Move on to the next batch of tests.

In the sample application scenario, a user may get a list of videos using the user interface by selecting a video type as the filter, or selecting "No Filter", and then clicking "Get Videos."

Clicking the "Get Videos" button raises the VideoListRequested event, passing the selected filter. The presentation model is then responsible for handling the event, populating its VideosList collection property, which the ListView control on the view is bound to. There are three expectations you need to test:

  • The CurrentVideoTypeFilter property on the presentation model should be set to the filter requested by the user.
  • When the user specifies "(No Filter)," the VideosList property should contain all the available videos.
  • When the user specifies a video type as the filter, the VideosList property should contain only videos that satisfy that filter.

Because this set of presentation model tests requires a different context, create a new test class for it. Doing so makes the intent of your tests clearer, and prevents you from falling into the anti-pattern trap of creating one test class per SUT. Note however that this article is about mocks and stubs, and not about best practices on writing and organizing tests. To learn more about the latter, I recommend you take a look into the Behavior-Driven Development style of tests (check out Scott Belware's article on the subject in the May/June 2008 issue of CoDe Magazine).

The test class for this context would initially look pretty much like the one you created before, with the stubs and presentation model being instantiated in a FixtureSetup method. However, things start to get a little more complicated here. First, you need to find a way to raise the VideoListRequested event on the view (since that's what happens when the user triggers that action on the UI). You certainly don't want to pop up a UI, and then click a button for that. Instead, you can change the StubView class you created previously, adding a method to it that can raise that event for you:

public void RaiseVideoListRequestedEvent( string filter) { VideoListRequested(this, new VideoListRequestedEventArgs(filter)); }

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date