Share via


Unit Testing

Exploring The Continuum Of Test Doubles

Mark Seemann

Code download available at:Testing2007_09.exe(271 KB)

This article discusses:

  • Unit testing with test doubles
  • Dummies, stubs, spies, fakes, and mocks
  • Manual and dynamic mocks
  • Knowing when and how to use each
This article uses the following technologies:
Unit Testing

Contents

Unit Testing for Dummies
Knowing When to Stub
Adding Spies to Your Tests
Making a Mockery of Unit Tests
Returning Values
Faking It
Manual Mocks
Mock Reality
Comparing Types

In the past couple of years, unit testing has gained tremendously in popularity; but while most developers understand the overall concept, certain aspects have been more elusive. Among these is how to effectively replace component servers for testing purposes. Most people call these replacements stubs or mocks, but as I will show in this article, these are only two types in a larger continuum of replacements.

Gerard Meszaros (xunitpatterns.com) has suggested the term "test double" (similar to the term "stunt double") as a general name for objects used to replace real component servers for testing purposes. While test double is in itself a generic term, specific names are used to describe the specific implementations, as explained in Figure 1.

Figure 1 Test Doubles Defined

Test Double Type Description
Dummy The simplest, most primitive type of test double. Dummies contain no implementation and are mostly used when required as parameter values, but not otherwise utilized. Nulls can be considered dummies, but real dummies are derivations of interfaces or base classes without any implementation at all.
Stub A step up from dummies, stubs are minimal implementations of interfaces or base classes. Methods returning void will typically contain no implementation at all, while methods returning values will typically return hard-coded values.
Spy A test spy is similar to a stub, but besides giving clients an instance on which to invoke members, a spy will also record which members were invoked so that unit tests can verify that members were invoked as expected.
Fake A fake contains more complex implementations, typically handling interactions between different members of the type it's inheriting. While not a complete production implementation, a fake may resemble a production implementation, albeit with some shortcuts.
Mock A mock is dynamically created by a mock library (the others are typically produced by a test developer using code). The test developer never sees the actual code implementing the interface or base class, but can configure the mock to provide return values, expect particular members to be invoked, and so on. Depending on its configuration, a mock can behave like a dummy, a stub, or a spy.

Although these types seem distinct in theory, the differences become more blurred in practice. For that reason, I think it makes sense to think of test doubles as inhabiting a continuum, as illustrated in Figure 2. At the one extreme you'll find dummies with absolutely no implementation, and at the other end are full production implementations. Dummies and production implementations are both well-defined, but stubs, spies, and fakes are more difficult to pin down: when does a test spy become a fake? In addition, mocks inhabit a rather large interval in the continuum, since they can be quite complex in some instances but very simple in others.

Figure 2 Spectrum of Test Doubles

Figure 2** Spectrum of Test Doubles **(Click the image for a larger view)

If this sounds too philosophical, it's really not, and I'll use a lot of examples throughout the rest of the article to illustrate the concepts. Due to space considerations, I have elected not to show a few classes here and there, but only for trivial implementations. If you feel that you are missing out on anything, the complete code is contained in the download for the article, available from the MSDN® Magazine Web site.

Unit Testing for Dummies

While mocks can be said to inhabit a slightly more extreme place on the test double continuum than dummies, the latter are far easier to understand, so I'll start by giving an example involving a dummy.

In a particularly unimaginative moment, I decided to use the tried and true online shop order-handling scenario for my example. Consider the simple Order class reproduced in Figure 3. The most important thing to notice is that the constructor takes an instance of IShopDataAccess, which is defined as:

Figure 3 Order Class

public class Order { private int orderId_; private IShopDataAccess dataAccess_; private OrderLineCollection orderLines_; public Order(int orderId, IShopDataAccess dataAccess) { if (dataAccess == null) { throw new ArgumentNullException("dataAccess"); } this.orderId_ = orderId; this.dataAccess_ = dataAccess; this.orderLines_ = new OrderLineCollection(this); } public OrderLineCollection Lines { get { return this.orderLines_; } } public void Save() { this.dataAccess_.Save(this.orderId_, this); } internal IShopDataAccess DataAccess { get { return this.dataAccess_; } } }

public interface IShopDataAccess { decimal GetProductPrice(int productId); void Save(int orderId, Order o); }

To unit test the Order class, I'll need to provide a test double for IShopDataAccess, since the constructor will throw an exception if I pass in null. The simplest test double I can create is a dummy, and Visual Studio® 2005 makes this very easy: I just create a new class in the unit test project, call it DummyShopDataAccess, and have it implement IShopDataAccess. In Visual Studio, I can then click on the smart tag for the interface name and select "Implement interface IShopDataAccess," which causes Visual Studio to create all the members of the interface. All member implementations are identical except for their signatures, so I'll just show the Save method here:

public void Save(int orderId, Order o) { throw new Exception("The method or operation is not implemented."); }

Creating a dummy with Visual Studio takes less than 10 seconds, and you only have to write a single line of code regardless of the number of members of the class: the class declaration containing the interface declaration.

Now that I have the DummyShop DataAccess class, it's easy to write a simple unit test of the Order class:

[TestMethod] public void CreateOrder() { DummyShopDataAccess dataAccess = new DummyShopDataAccess(); Order o = new Order(2, dataAccess); o.Lines.Add(1234, 1); o.Lines.Add(4321, 3); Assert.AreEqual<int>(2, o.Lines.Count); // More asserts could go here... }

At this point, the dummy suffices since the IShopDataAccess interface isn't being used in any of the methods exercised on the test target, so I only need it to get past that insistent input validation in the constructor. Obviously, as soon as I attempt to invoke a member that actually uses the IShopDataAccess instance (such as the Save method), my tests would come crashing down if I were to use DummyShopDataAccess, since it would throw an exception if invoked. For these tests I can use one of the other test double types, as I'll proceed to demonstrate.

Knowing When to Stub

As soon as your unit test invokes a member on the test target that in turn invokes a member on a test double, you need something that, at a minimum, doesn't throw an exception. The simplest type of test double that meets this criterion is a stub.

In my next example, I'll write a unit test that invokes the Save method on the Order class. This will in turn invoke the Save method on the IShopDataAccess interface, so I'll need to implement the Save method in such a way that it doesn't throw an exception. Since this method returns void, the implementation is very simple:

public void Save(int orderId, Order o) { }

If you are interested in semantics, you may start to wonder whether this implementation is actually a dummy or a stub. It would be a stretch to claim that there's much implementation in this line of code, and if I leave the other members at throwing exceptions, you could very well argue that this test double is, in fact, a dummy. On the other hand, dummies are supposed to be used only to fill in required parameters, but this implementation can be used for something else since a client can invoke its save method without crashing; thus you could argue that this implementation is a stub.

While it would probably be possible to propose a stronger definition of either dummy or stub, I find that this ambiguity has few practical consequences aside from naming. It does, however, nicely illustrate why I think that test doubles inhabit a continuum instead of being discreet types. In this particular case, I'll choose to call my new class StubShopDataAccess, since I may want to add more implementation to some of the other members later.

With StubShopDataAccess in place, I can now write the following unit test:

[TestMethod] public void SaveOrder() { StubShopDataAccess dataAccess = new StubShopDataAccess(); Order o = new Order(3, dataAccess); o.Lines.Add(1234, 1); o.Lines.Add(4321, 3); o.Save(); }

Calling Save on the Order class will invoke the Save method on StubShopDataAccess, which does absolutely nothing. Accordingly, the test succeeds simply due to the fact that it doesn't fail, but it doesn't really verify the test target.

Adding Spies to Your Tests

Although investigating test doubles is the subject of this article, keep in mind that the purpose of a unit test is to demonstrate that the test target (in this case, the Order class) works as intended. For the Save method, the intention is that it should invoke the Save method on the IShopDataAccess interface, but the test defined earlier doesn't verify this, since it would also succeed if Order.Save had a blank implementation—in other words, if it didn't invoke IShopDataAccess.Save.

In order to verify that the Save method was called, the test double should record whether it was invoked or not. Since recording member invocations for later verification is the primary distinguishing trait of a test spy, I'll create a new class and call it SpyShopDataAccess, as illustrated in Figure 4.

Figure 4 Spy Class

internal class SpyShopDataAccess : IShopDataAccess { private bool saveWasInvoked_; #region IShopDataAccess Members // Other IShopDataAccess members ommitted for brevity public void Save(int orderId, Order o) { this.saveWasInvoked_ = true; } #endregion internal bool SaveWasInvoked { get { return this.saveWasInvoked_; } } }

Once again, I want to point out that the test spy type doesn't occupy a discreet space in the test double continuum. Given this, what if the other method of the IShopDataAccess interface still throws an exception? Is it half dummy and half spy, then? Or would it mostly resemble a stub if the other method provided a hardcoded return value but didn't record that it was invoked?

Even when recording invocations in all members, there are different ways to do it. In Figure 4, I used the simplest approach by just setting a flag, but I could also have recorded the number of invocations, like this:

private int saveInvocationCount_; public void Save(int orderId, Order o) { this.saveInvocationCount_++; } internal int SaveInvocationCount { get { return this.saveInvocationCount_; } }

This implementation clearly captures more information than the first one, but does that mean that it's more spy than the other spy? It's possible to create even more advanced spies by also recording the input parameters for every call, but that requires more code to implement and would thus place such a spy further to the right on the test double continuum. Once again, the point is that the boundary between stubs and spies is fuzzy and it makes sense to view test doubles as inhabiting a continuum.

With the SpyShopDataAccess class, I can now write a unit test that verifies that the Order is, in fact, being saved:

[TestMethod] public void SaveOrderWithDataAccessVerification() { SpyShopDataAccess dataAccess = new SpyShopDataAccess(); Order o = new Order(4, dataAccess); o.Lines.Add(1234, 1); o.Lines.Add(4321, 3); o.Save(); Assert.IsTrue(dataAccess.SaveWasInvoked); }

This simple test only verifies that the Save method was invoked, but not how many times or which parameters were used. It will often be a good idea to verify this type of data as well, but then you will have to either write a more advanced test spy or resort to mocks, which typically record all calls by default.

Making a Mockery of Unit Tests

Mocks are very different beasts than dummies, stubs, spies, and fakes, which are all created by the test developer. A mock is created by calling methods on a dynamic mock object that is being created at run time.

In my October 2004 MSDN Magazine article, "Unit Testing: Mock Objects to the Rescue! Test Your .NET Code with NMock" (msdn.microsoft.com/msdnmag/issues/04/10/NMock), I described the basics of dynamic mocks and how to use a particular mock library called NMock. Here, I will use another free mock library called Rhino Mocks (available at ayende.com/projects/rhino-mocks.aspx).

Instead of creating a new code file that implements the IShopDataAccess interface, I'll instruct Rhino Mocks to create an object implementing the interface at run time, as shown in Figure 5. To work as intended, a mock needs to be configured to expect which members will be invoked on the interface. Mock libraries do this in different ways, but Rhino Mocks works by allowing you to start out by recording expectations. Essentially, the mock has two modes: a recording mode where the unit test defines expectations, and a replay mode where clients can invoke members on the mock.

Figure 5 Dynamic Mock

[TestMethod] public void SaveOrderAndVerifyExpectations() { MockRepository mocks = new MockRepository(); IShopDataAccess dataAccess = mocks.CreateMock<IShopDataAccess>(); Order o = new Order(6, dataAccess); o.Lines.Add(1234, 1); o.Lines.Add(4321, 3); // Record expectations dataAccess.Save(6, o); // Start replay of recorded expectations mocks.ReplayAll(); o.Save(); mocks.VerifyAll(); }

In the example in Figure 5, it's simple to set up the expectations, since the only thing I expect is a single call to the Save method. After all expectations have been recorded, I switch the mock to replay mode by calling ReplayAll.

When I call Save on the Order object, it will subsequently invoke the Save method on the mock. And since the mock is in replay mode, it will record that the method was called with the expected parameters. If the call to Save is made, but with different parameters, the mock will throw an exception. Calling VerifyAll at the end ensures that the Save method was invoked on the mock at all—if not, an exception is thrown.

Since the mock verifies that the expected calls were made, it works much like a test spy. However, it only exists at run time, so it's difficult to pin down exactly where on the test double continuum a mock exists. That's why I prefer to think of mocks as inhabiting quite a large part of the continuum.

Returning Values

Until now, I've only looked at mocking a single method (Save) that doesn't return any value. As I've shown you already, implementing a stub for a method that returns void can be very simple. For methods that return values, things get a tiny bit more complicated. To show you how, I'll expand on the Order example.

The Order class contains a collection of OrderLine objects. Each OrderLine object contains a productId, a quantity, and a reference to the Order that owns it. To calculate each line total, the OrderLine class contains the Total property:

private decimal? total_; public decimal Total { get { if (!this.total_.HasValue) { decimal unitPrice = this.owner_.DataAccess.GetProductPrice( this.productId_); this.total_ = unitPrice * this.quantity_; } return this.total_.Value; } }

Notice that this implementation calls the GetProductPrice on IShopDataAccess. The implication of this is that to test the Total property, any test double used must return a value from GetProductPrice. The simplest way to do this is just to return a hard-code value:

public decimal GetProductPrice(int productId) { return 25; }

With this implementation in StubShopDataAccess, I can now write a unit test:

[TestMethod] public void CalculateSingleLineTotal() { StubShopDataAccess dataAccess = new StubShopDataAccess(); Order o = new Order(7, dataAccess); o.Lines.Add(1234, 2); decimal lineTotal = o.Lines[0].Total; Assert.AreEqual<decimal>(50, lineTotal); }

Since the first order line has a quantity of 2 of the product 1234, the line total becomes 50, since the unit price returned by StubShopDataAccess is 25.

Although this implementation is very simple, it's not particularly flexible since it's not possible to test an Order with several order lines where each product has a different unit price. One possible solution is obviously to change the implementation of GetProductPrice, like this:

public decimal GetProductPrice(int productId) { switch (productId) { case 1234: return 25; case 2345: return 10; default: throw new ArgumentException("Unexpected productId"); } }

Notice how conditional logic has crept into the test double, making the implementation a bit more complex. At this point you might begin to wonder whether it's still a stub: it's returning hardcoded values, but it contains conditional logic.

In principle, you can add an arbitrary number of case statements, but as you may have noticed from the previous unit test example, it's not very clear just from looking at the test code what the stub is going to return.

Faking It

Creating a more configurable test double may be the way to go in some instances. In the case of IShopDataAccess, this interface is really an abstraction of a database, so one approach would be to create a primitive, in-memory database. The FakeShopDataAccess class shown in Figure 6 is about as primitive as it gets since it has no referential integrity or any other features normally desirable in a database. It contains a collection object that basically acts as a stand-in for a database table. Since I need to be able to index products on product ID, I've created a ProductCollection class (not shown) that simply derives from KeyedCollection<int, Product>; the Product class is basically a property bag of product data and is another test-only class created solely for this purpose.

Figure 6 Primitive Test Database

internal class FakeShopDataAccess : IShopDataAccess { private ProductCollection products_; internal FakeShopDataAccess() { this.products_ = new ProductCollection(); } #region IShopDataAccess Members public decimal GetProductPrice(int productId) { if (this.products_.Contains(productId)) { return this.products_[productId].UnitPrice; } throw new ArgumentOutOfRangeException("productId"); } public void Save(int orderId, Order o) { } #endregion internal IList<Product> Products { get { return this.products_; } } }

You might notice that FakeShopDataAccess doesn't save any order passed to its Save method. This was a shortcut I chose to take since the IShopDataAccess interface doesn't have any members that return saved orders. However, if I had wanted to use this class for verification of the Save method, I could have added the Order object to an internal dictionary and later verified the contents of that dictionary. This would have added a test spy trait to the fake, once again illustrating that the boundaries between the different test double types are blurred.

With the FakeShopDataAccess class, it now becomes more apparent what's going on in the data access layer, as you can see in Figure 7. Before creating the test target (the Order object), I create and populate the list of products and their unit prices. When the test executes, GetProductPrice returns the unit price for the requested product in the list.

Figure 7 Testing with the Fake Database

[TestMethod] public void CalculateLineTotalsUsingFake() { FakeShopDataAccess dataAccess = new FakeShopDataAccess(); dataAccess.Products.Add(new Product(1234, 45)); dataAccess.Products.Add(new Product(2345, 15)); Order o = new Order(9, dataAccess); o.Lines.Add(1234, 3); o.Lines.Add(2345, 2); Assert.AreEqual<decimal>(135, o.Lines[0].Total); Assert.AreEqual<decimal>(30, o.Lines[1].Total); }

Notice how this test is more readable than a similar test utilizing a stub since you don't have to flip to the stub code to see which values it will return. The disadvantage of creating a fake is that it may turn out to be a bit of work in itself. In the case of FakeShopDataAccess, I ended up creating three classes: FakeShopDataAccess itself, the ProductCollection class, and the Product class. The IShopDataAccess interface is even pretty simple, so for more complex interfaces, this disadvantage is only more pronounced.

Manual Mocks

One of the golden rules of unit testing is that you are supposed to write some fairly simple code to test your complicated code. Creating complex fakes would somewhat defy that purpose. If you ever begin thinking about unit testing the fake itself, you have probably overdone it.

Dynamic mocks are the ultimate answer to this conundrum, but you may not always want to use a full mock library. A mock library is an extra assembly that you need to reference in your test project, and if you are working on a team-based project, you need to address the issue of standardizing on a common mock library, installing it on all developer machines (or putting the binary under source control), and all the other details that go along with application deployment. That's why I sometimes like to throw together something I call a manual mock.

Let's say that I want to rewrite the test in Figure 7 without using a fake, but still keeping my test double translucent. The only member of IShopDataAccess I care about for this test is the GetProductPrice method, so I create the manual mock class shown in Figure 8 to handle this particular method. Notice that the constructor takes a Converter<int, decimal> that it saves and uses to implement the GetProductPrice method.

Figure 8 Manual Mock Class

internal class ProductPriceMockShopDataAccess : IShopDataAccess { private Converter<int, decimal> implement_; internal ProductPriceMockShopDataAccess( Converter<int, decimal> productPriceCallback) { this.implement_ = productPriceCallback; } #region IShopDataAccess Members public decimal GetProductPrice(int productId) { return this.implement_(productId); } public void Save(int orderId, Order o) { throw new NotImplementedException(); } #endregion }

Converter<int, decimal> is a delegate that takes an int as input and returns a decimal. If you refer to the definition of IShopDataAccess, you will notice that this is the same signature as GetProductPrice. This means that I can use Converter<int, decimal> to implement GetProductPrice, and that's exactly what I'm doing in Figure 8.

This manual mock contains no implementation in itself, but is just a scaffolding to be used by the unit test, as shown in Figure 9. In this unit test, I initialize the mock with an anonymous method that will get invoked when the GetProductPrice method is called. This is essentially similar to how dynamic mocks are configured, where you first define expectations for the mock, and then subsequently create and exercise the test target.

Figure 9 Using the Manual Mock

[TestMethod] public void CalculateLineTotalsUsingDelegate() { ProductPriceMockShopDataAccess dataAccess = new ProductPriceMockShopDataAccess(delegate(int productId) { switch (productId) { case 1234: return 45; case 2345: return 15; default: throw new ArgumentOutOfRangeException("productId"); } }); Order o = new Order(10, dataAccess); o.Lines.Add(1234, 3); o.Lines.Add(2345, 2); Assert.AreEqual<decimal>(135, o.Lines[0].Total); Assert.AreEqual<decimal>(0, o.Lines[1].Total); }

While the unit test in Figure 9 may look more complicated than the test using a fake shown in Figure 7, looks can be deceiving. Although the test itself is more verbose, the total size of the code supporting the test is smaller, since the fake requires three supporting classes (including the fake itself), while the manual mock only requires the mock itself.

The nice thing about using delegates for mocking is that the test developer writes a new implementation for each unit test. This not only enables you to change return values and behavior, but also to decide on the level of spying you want to perform. If you only want to return values, you can get by with code similar to that shown in Figure 9, but if you also want to record method calls for later verification, you can easily do this as well since anonymous methods allow access to outer variables.

The main drawback of the ProductPriceMockShopDataAccess class shown in Figure 8 is that it only mocks the GetProductPrice method, but throws a NotImplementedException in the other member. Obviously, you can take the concept of manual mocks a couple of steps further by generalizing the approach. One thing you could do in a general-purpose manual mock for IShopDataAccess is to define a Converter<TInput, TOutput> for every member of the interface, but that can soon become unwieldy if your interface has many members.

A better approach would be to define a delegate that can be used to implement any member, like this one:

public delegate void ImplementationCallback(MemberData member);

This delegate simply defines a method that returns void and takes a MemberData object as input. The MemberData class is rather trivial, so I'll not use space to show it here, but it is included in this article's code download. It is basically a property bag that contains the name of the invoked member, a list of parameter values, and a property that can be used to set any return value an implementation may need to provide. The return value is optional since not all methods need a return value (for instance, the Save method returns void).

With the ImplementationCallback delegate, I can create the more general manual mock shown in Figure 10. As before, the constructor is used to populate the mock with a delegate that will contain the implementation. Each member implementation follows a common pattern: a MemberData object is constructed to contain the name of the invoked method as well as the name and value of any parameters. The member variable is then used as a parameter when invoking the delegate. If the method should return any value, the value is extracted from the member variable and returned. It's the responsibility of the implementing delegate to set the ReturnValue property.

Figure 10 Generalized Manual Mock

internal class MockShopDataAccess : IShopDataAccess { private ImplementationCallback implement_; internal MockShopDataAccess(ImplementationCallback callback) { this.implement_ = callback; } #region IShopDataAccess Members public decimal GetProductPrice(int productId) { MemberData member = new MemberData("GetProductPrice"); member.Parameters.Add(new ParameterData("productId", productId)); this.implement_(member); return (decimal)member.ReturnValue; } public void Save(int orderId, Order o) { MemberData member = new MemberData("Save"); member.Parameters.Add(new ParameterData("orderId", orderId)); member.Parameters.Add(new ParameterData("o", o)); this.implement_(member); } #endregion }

A test using this approach looks similar to the previous test, as shown in Figure 11. The MemberData contains information about the member that was invoked, so it's sensible to perform an initial check whether the expected method is being called and throw an exception if this is not the case. If the method invoked was the GetProductPrice method, the test code then sets the relevant return value on the MemberData input parameter. This is the value that MockShopDataAccess will then return to its caller.

Figure 11 Using the Generalized Mock

[TestMethod] public void CalculateLineTotalsUsingManualMock() { MockShopDataAccess dataAccess = new MockShopDataAccess(delegate(MemberData member) { if (member.Name == "GetProductPrice") { int productId = (int)member.Parameters["productId"].Value; switch (productId) { case 1234: member.ReturnValue = 45m; break; case 2345: member.ReturnValue = 15m; break; default: throw new ArgumentOutOfRangeException( "productId"); } } else { throw new InvalidOperationException("Unexpected member"); } }); Order o = new Order(11, dataAccess); o.Lines.Add(1234, 3); o.Lines.Add(2345, 2); Assert.AreEqual<decimal>(135, o.Lines[0].Total); Assert.AreEqual<decimal>(30, o.Lines[1].Total); }

You may have noticed that creating such a general-purpose manual mock follows a repeatable pattern: first, create the type and its members; then, in each member, create a MemberData object, invoke the delegate and return the ReturnValue property, if applicable. This implementation pattern can be automated, so it's possible to use a bit of Reflection and CodeDOM logic to create and emit a run-time object that works exactly like that. While that is beyond the scope of this article, I've provided a primitive proof of concept demonstrating this approach in the code download. Here, the DelegateMock class can create delegate-based mocks on the fly. The unit test looks almost identical to the test in Figure 11, except for the line where the mock is created, which now appears like this:

IShopDataAccess dataAccess = DelegateMock.Create<IShopDataAccess>( delegate(MemberData member){...});

Just like a regular mock, this manual mock is dynamically created and exists only at run time. In fact, the only difference between DelegateMock and dynamic mocks lies in how they implement the interface or base class they are mocking.

The advantage of delegate-based mocks lies in the high degree of freedom they offer and the relatively low barrier of entry. While many developers find dynamic mocks difficult to understand and learn, more developers understand delegates and know how to write code. Since all expectations, return values, recording of invocations, and so on have to be written explicitly in code, most developers already know how to accomplish those tasks without having to learn a new object model.

Mock Reality

While there are advantages to manual mocks, there are certainly also disadvantages, one of which is that providing delegate-based implementations in every unit test quickly becomes rather verbose—particularly when you also need to record invocations like a test spy. Perhaps the biggest disadvantage, however, is that writing delegates as implementations can rapidly become rather tedious and error-prone work.

Although I find manual mocks handy as quick-and-dirty mocks, it is no secret that I personally prefer dynamic mocks. A good mock library will create mocks that are easy to configure and that act as test spies at the same time.

The line total example that I've shown variations of in this article is very easy to write using Rhino Mocks. Creating the mock itself is accomplished in two lines of code and no supporting classes:

MockRepository mocks = new MockRepository(); IShopDataAccess dataAccess = mocks.CreateMock<IShopDataAccess>();

The dataAccess variable can then be used to create a new Order instance, just like the previous examples. The mock is created in recording mode, so defining expectations and return values can be done in only two lines of code:

Expect.Call(dataAccess.GetProductPrice(1234)).Return(45m); Expect.Call(dataAccess.GetProductPrice(2345)).Return(15m);

The first line simply states that the mock should expect a call to the GetProducPrice method with the value 1234, and, when that happens, it should return 45. The second line obviously sets up a similar expectation for the product ID 2345.

By default, the mock acts as a test spy, so without further work, you can just call mocks.VerifyAll at the end of the test to verify that all expectations were met.

Comparing Types

In Figure 12, I've listed the different types of test doubles and their advantages and disadvantages. While such a table seems to indicate that the types are discreet and different, I hope that this article has demonstrated that the boundaries are, in fact, vague and that all test doubles are inhabitants of a continuum. It may seem that this distinction is mostly of semantic value, but it is my firm belief that the words we use shape how we think about abstract concepts, so it's important to be as precise as possible when describing such phenomena.

Figure 12 Advantages and Disadvantages of Test Doubles

Test Double Type Advantages Disadvantages
Dummy Very easy to create. Not very useful.
Stub Easy to create. Limited flexibility. Opaque when observed from unit tests. No ability to verify that members were invoked correctly.
Spy Can verify that members are invoked correctly. Limited flexibility. Opaque when observed from unit tests.
Fake Offers a semi-complete implementation that can be used in many different scenarios. Harder to create. May be so complex that it requires unit testing in itself.
Mock Efficient creation of test doubles. Can verify that members are invoked correctly. Transparent when observed from unit tests. Steeper learning curve.

In my opinion, a good dynamic mock library can replace dummies, stubs, and spies, at least for the majority of situations. If your circumstances require creating a test double for a very complex interface or base class, there may be times where fakes offer more productivity than mocks.

Mocks need to have all expectations explicitly defined in each and every test. As the interaction between the test target and the test double becomes more intense, the work involved in defining a myriad of expectations may become too tedious and labor-intensive compared to the effort required to write a workable fake. Identifying the point at which this happens is, unfortunately, more an art form than an exact science.

Mark Seemann works for Microsoft Services in Copenhagen, Denmark, where he helps Microsoft customers and partners architect, design, and develop enterprise-level applications. Mark can be reached via his blog at blogs.msdn.com/ploeh.