This is the second part of the 2 post series on Mocking. In the last post, we discussed the basics of Mocking and how Adam (the intern) used Manual Mocking to unit test the simple Shopping Cart application he’s building. In this post, we’ll see how using a Mocking library can make the process of mocking out dependencies simpler.
Why use a mocking library?
The projects you work on are usually nowhere as simple/small as the Shopping Cart example Adam is working on (If they are, is there room for one more on the team?). If you have to build a new mock class for each of your classes, the amount of new code you have to write increases significantly. Also, you might want to reuse your test infrastructure across different classes, or just want the mock object to act dumb.
It’s also harder to do interaction based testing using Manual Mocks. In the manual mock for Logger, we had to add a new field to track how many times it is called. As the behavior to test becomes complicated, so does your mock class. Using a mocking library can take care of all these mundane things.
Why MOQ?
MOQ is a simple and easy to learn mocking library for .NET that takes advantage of .NET 3.5 (i.e. Linq expression trees) and C# 3.0 features (i.e. lambda expressions). There are other mocking libraries like RhinoMocks and TypeMock Isolator, and MOQ is relatively new to the scene. The main reason I recommend using MOQ is because it has a really low learning curve and is intuitive to use. Unlike other libraries, it does not force you to distinguish between a mock and a stub, which makes it easier for developers new to mocking to work with.
MOQ Basics
To create a mock of type T, you will use the Mock
Creating Mock objects
Mock<IProduct> _mockProduct = new Mock<IProduct>();
Mock<ILogger> _mockLogger = new Mock<ILogger>();
Mocking a method implementation and returning fake data
_mockProduct.Setup(m => m.GetProductCategory()).Returns("Test Category");
Mocking a property and returning fake data
_mockProduct.SetupGet(m => m.Name)
.Returns("Product 1");
Throw exception
_mockLogger.Setup(m => m.Log(It.Is<string>(p => p == null)))
.Throws(new ArgumentNullException());
Validate Parameters
_mockLogger.Setup(m => m.Log(It.IsRegex("[1-9]+")))
.Callback(() => Console.WriteLine("Numbers passed"));
Verify Interactions
_mockLogger.Object.Log("test");
_mockLogger.Verify(m => m.Log(It.Is<string>(s=>s=="test")),Times.Once());
Invoke Callback
int counter = 0;
_mockLogger.Setup(m => m.Log(It.IsAny<String>())).Callback(() => counter++);
Even if you are new to MOQ, you’ll find the API very intuitive and easy to pick up.
Back to the Shopping Cart
So Adam sees how easy it is easy to work with MOQ and how much code/effort it saves him, and he updates his unit tests for the Shopping Cart accordingly. Note that these unit tests are independent of the manual mock classes as well as from external resources like the filesystem/database.
1: [TestMethod]
2: public void AddProduct_AddingProductWithPrice10_ShouldMakeTotal10()
3: {
4: var mock = new Mock<IProduct>();
5: mock.SetupGet(m => m.Price).Returns(10.00M);
6: var cart = new ShoppingCart(null);
7: cart.AddProduct(mock.Object);
8: Assert.AreEqual(10.00M, cart.Total);
9:
10: }
11:
12: [TestMethod]
13: public void AddProduct_AddingProduct_ShouldCallLogger()
14: {
15: var mockProduct = new Mock<IProduct>();
16: var mockLogger = new Mock<ILogger>();
17: mockLogger.Setup(m => m.Log(It.IsAny<string>())).Verifiable("Log should have been called");
18: var cart = new ShoppingCart(mockLogger.Object);
19: cart.AddProduct(mockProduct.Object);
20: mockLogger.Verify();
21: }
Later….
You are satisfied with the ShoppingCart project and the corresponding tests that Adam built. When you run into him in the evening, you commend him on the nice work and invite him to have a couple of beers with you. As you are sitting at the bar, you glance at the mirror and you see something that makes the hair at the back of your neck stand up. Instead of Adam and you at the bar, it’s just you sitting there with 2 beers in your hand talking to yourself. You then realize that Adam was a figment of your imagination and you might have suffered from some sort of a dual personality disorder (Kind of like Fight Club)). Your brain created Adam so that you could face your fears and misconceptions about Unit Testing and Mocking. On the bright side, you realize that you have finished your project and are sitting in a bar with 2 beers. Cheers!