Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Isolating Dependencies in Tests Using Mocks and Stubs : Page 5

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


advertisement

State-Based and Interaction-Based Tests

At this point you may be thinking: "I thought this article was about mocks and stubs, but so far I've only seen stubs!" So it's time you have a look at mocks.

Stubs are the type of fake objects that help you with state-based testing.

Up to this point, your tests can be considered "state-based" tests: you act upon your system under test, and then you check its state to make sure it matches your expectations. The first batch of tests checked the state of the newly instantiated presentation model to answer questions such as "Does it have references set to the appropriate objects?" or, "Does it have some properties set to some specific defaults?" The second batch of tests checked the state of the presentation model, more specifically the state of its VideosList property, to make sure the list gets populated properly after the presentation model has handled an event raised by the view. Stubs are the type of fake objects that help you with state-based testing. They are either filled with values to be read by the SUT to perform its duties, or the SUT sets their values, which the test then checks.



In other situations, state-based tests aren't possible, or aren't easy to write (for example, there may not be a state to check for). This type of situation comes up when the SUT has dependencies and you need to test the interaction of the SUT with those dependencies. In such cases, you can resort to interaction-based tests. Mocks are the type of fake objects that help you with interaction-based testing. They act much like spies, watching expectations set by the test on the mock, and then reporting these expectations back to the test.

Interaction-Based Tests Using Static Fake Objects

In the sample application, the VideosListPresentationModel is responsible for showing its view when you call its ShowView method:

public void ShowView() { View.Show(); }

The ShowView method shows the view by sending a message to it; that is, it calls the view's Show method. The test for that behavior has to check for the interaction between the presentation model and its view. To write such a test using a static mock object, you need to write some code, so that whenever methods get called you flip a Boolean field to indicate that the message has been received. The test can then check the value of that field to see whether the expectation has been met.

Start by creating a static mock class that implements your IVideosListView interface:

public class MockView : IVideosListView { public bool ShowWasCalled; public void Show() { ShowWasCalled = true; } // other members code elided }

The important information there is that you define a Boolean ShowWasCalled property, which gets set to true by the Show method whenever that method gets called.

Your test class for that behavior looks pretty much like the ones you've created earlier in this article when you were using static stub classes, except that now you instantiate the MockView class, as opposed to the StubView class:

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

The following snippet shows the test method for that expected behavior:

[TestMethod] public void ShowView_should_display_the_view() { PM.ShowView(); Assert.IsTrue(View.ShowWasCalled); }

Not much there: you act on the presentation model by calling its ShowView method, and you check the ShowWasCalled property on the "mock" View to make sure the message was sent to that object, thereby validating the interaction between the two objects.

So how would you write an interaction-based test using dynamic mocks?

Interaction-Based Tests Using Dynamic Fake Objects

You can rewrite the test to use dynamic fake objects (which means you can get rid of that MockView class you created in the previous section). The code snippet below highlights the main aspects of building a test class that uses dynamic fake objects:

private static VideosListPresentationModel PM; private static IVideosListView View; private static IVideoRepository Repository; [ClassInitialize()] public static void FixtureSetup(TestContext tc) { View = MockRepository.GenerateMock<IVideosListView>(); Repository = MockRepository.GenerateStub<IVideoRepository>(); PM = new VideosListPresentationModel(View, Repository); }

Type both the View and Repository fields to their respective interfaces. The Repository field gets an instance of a stub generated by Rhino Mocks; whereas, the View field gets an instance of a mock generated by Rhino Mocks. I'll explain what the difference is between a stub and a mock shortly.

Here's how the test method should look now:

[TestMethod] public void ShowView_should_display_the_view() { PM.ShowView(); View.AssertWasCalled(v => v.Show()); }

AssertWasCalled is another one of those nice extension methods exposed by the Rhino.Mocks namespace. You call that method on the View to assert that a given method was called on it; in this case, the Show method. That eliminates the need to create a separate class manually, create a Boolean field, and then set that field within the method you expected to be called! Rhino Mocks takes care of inspecting the object to make sure the expected method was called.

Mocks are the type of fake objects that help you with interaction-based testing.

So what happens if you break the ShowView method, by removing the call to View.Show(). The test would then fail, and Rhino Mocks would give you the following error message:

TestCase 'VideosListPresentationModel_ViewDisplay_Tests. ShowView_should_display_the_view' failed: Rhino.Mocks.Exceptions.ExpectationViolation Exception: IVideosListView.Show(); Expected #1, Actual #0.

In other words, the test failed because you expected one call to IVideosListView.Show(), but didn't get any.

GenerateMock vs. GenerateStub

So what's the difference between the GenerateMock and GenerateStub methods? The dynamic objects created by both methods are very similar. The main difference is that mock objects can cause a test to fail when expectations set on it aren't met. A stub, on the other hand, won't cause a test to fail if expectations set on it aren't met.

You can learn more about these subtle differences reading the documentation on Rhino Mocks. Also make sure to read Martin Fowler's "Mocks Aren't Stubs" article.

When to Use Dynamic Fake Objects

Dynamic fake objects are not silver bullets. They certainly help in many scenarios, but may not be very helpful in others. Sometimes, configuring the dynamic mock is so hard and/or complex that it's better to just create a simple static fake object for it. It's up to the developer to analyze the situation and decide which technique will produce a test that is easier to write, understand, and maintain.

Also, there are situations where a mock framework may have limitations that can rule out its use. For example, Rhino Mock can mock only virtual members, and it cannot mock sealed classes. Many developers would argue that members should always be virtual, and mocking sealed classes shouldn't be necessary if classes have been designed properly—but you may be in a situation where you just can't afford the time or money to make changes to your design to make your classes more testable.

TypeMock (the commercial framework mentioned earlier), on the other hand, can mock pretty much everything: sealed classes, private and non-virtual members, etc. That's definitely useful when you're writing tests for a legacy code base that wasn't designed with testability in mind, and when fixing the design and implementation is out of question, but it may also spoil you, and lead you to write sloppy code.

I really don't want to dive into that discussion because a lot has already been said about it on the web. I'll leave it up to readers to research. I just wanted to briefly mention these things so you are aware of their existence.

This article covered the isolation of dependencies when writing unit tests by using fake objects. By isolating those dependencies you end up with unit tests that become more informative in case things go wrong, because you're more likely to be tracking a bug in your SUT, as opposed to tracking a bug that may stem from the SUT's dependencies. Fake objects also help the unit test to become solely a "unit" test, causing tests to run faster and be independent of outside resources such as files on disk, databases, web services, etc.

Mock frameworks help you keep your code base clean, eliminating the need to create and maintain extra individual classes for static fake objects.

You've seen some basic scenarios, the main differences between static and dynamic fake objects, as well as state-based and interaction-based tests. You've also covered a little of how to use Rhino Mocks for dynamic fake objects. To be fair, Rhino Mocks deserves an entire article that would show how to leverage the framework when writing tests for more complex behaviors.

Useful Resources

I recommend these resources as a starting point for digging more deeply into writing unit tests using mocks and stubs to isolate SUT dependencies:



Claudio Lassala is a Senior Developer at EPS Software Corp. He has presented lectures at Microsoft events such as PDC Brazil and various other Microsoft seminars, as well as several conferences and user groups across North America and Brazil. He is a multiple winner of the Microsoft MVP Award since 2001 (for Visual FoxPro in 2001-2002, and for C# ever since), an INETA speaker, and also holds an MCSD for .NET certification. He has published articles in MSDN Brazil Magazine, CODE Magazine, UTMag, Developers Magazine, and FoxPro Advisor. You can read his blog or follow him on Twitter.
Comment and Contribute

 

 

 

 

 


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

 

 

Sitemap