Today I was procrastinating by reading unanswered questions at StackOveflow, when I noticed a question about what Mocks are. I though I could give a short answer, but I got carried away and gave a long one.
Only after posting it did I notice that the guy that asked the question is not a member anymore and the question is from 2009.
Probably it won’t be read by anyone there so maybe someone here will read it and find it useful. It’s about the difference between mocks and stubs, and why isolation frameworks are useful. It also addresses the differences between unit and integration testing, here it is:
To really understand Mocks you first have to grasp four concepts
What is interaction testing
What are isolation frameworks (like rhino mocks)
What is a fake
What is a stub
And finally what is a mock
Interaction testing is about checking if a particular method (the one you are testing) calls another method in another class (the external dependency) with the parameters it is supposed to use.
Imagine you have a method in some class that logs every time it is called with invalid parameters. For clarifying the difference between stubs and mocks I’ve added 2 external dependencies (IStringAnalyzer and ILooger):
class SomeClass { IStringAnalyzer stringAnalizer; ILogger logger; public SomeClass(IStringAnalyzer stringAnalyzer, ILogger logger) { this.logger = logger; this.stringAnalyzer = stringAnalyzer; } public void SomeMethod(string someParameter) { if (stringAnalyzer.IsValid(someParameter)) { //do something with someParameter }else { logger.Log("Invalid string"); } } }
In this example you want to test if a method call to SomeClass’ SomeMethod with an invalid parameter calls the log method in ILogger with the string parameter “Invalid string”.
You *do not* want to use your “real” implementations of IStringAnalyzer and ILogger because these can have errors, and because this is *unit* testing you just want to test one thing at a time, if you test several things at a time what you are really doing is *integration* testing. The reason for only testing one thing at a time is that if your test fails you know immediately that it is failing because of that only one thing you tested.
You need to provide two alternative implementations, one for IStringAnalyzer and one for ILogger so that you can do this test properly. There will be a difference in these alternative implementations in terms of what they need to do. For IStringAnalyzer you just want that when it’s called it returns false, so that the method under test will go through the code path you want to test. You really don’t care about the parameter’s value (someParameter).
For ILogger’s log method you want to know that it was called, and that it was called with “Invalid string”, so that you can assert this in your test.
These two alternative implementations of IStringAnalyzer and ILogger are both called “fakes” (because they fake the external dependencies), but one is a Stub (IStringAnalyzer) and the other is a mock (ILogger). The stub is there only to get you to where you need to go in your test (in this case that IStringAnalyzer’s IsValid method returns false). The mock is there to allow you to check if the interaction with the external dependency was done properly (in this case ILogger). Some people refer to mocks (or to this type of mock) as a Test Spy (an infinitely better name in my opinion). And yes, there are other types of mocks (I’ve never used them though). A good source for this is Working with legacy code form Michael Feathers and Roy Osherove’s The art of unit testing.
You can create the stub and mock “by hand”, for example:
class StringAnalyzerStub : IStringAnalyzer { public bool whatToReturn; public StubStringAnalyzerStub(bool whatToReturn) { this.whatToReturn = whatToReturn; } public bool IsValid(string parameter) { return whatToReturn; } } class LoggerMock : ILogger { public string WhatWasPassedIn; public void Log(string message) { WhatWasPassedIn = message; } }
And here’s the test:
[Test] public void SomeMethod_InvalidParameter_CallsLogger { IStringAnalyzer s = new StringAnalyzerStub(false); //will always return false when IsValid is called ILogger l = new LoggerMock(); SomeClass someClass = new SomeClass(s, l); someClass.SomeMethod("What you put here doesnt really matter because the stub will always return false"); Assert.AreEqual(l.WhatWasPassedIn, "Invalid string"); }
The thing about doing this by hand is that it is error prone and hard to maintain, hence the need for isolation frameworks like Rhino Mocks. They allow you to create these mocks and stubs dynamically, here’s how the same test would look like using Rhino Mocks (using the arrange, act, assert syntax):
[Test] public void SomeMethod_InvalidParameter_CallsLogger { Rhino.Mocks.MockRepository mockRepository = new Rhino.Mocks.MockRepository(); IStringAnalyzer s = mockRepository.Stub<IStringRepository>(); s.Stub(s => s.IsValid("something, doesnt matter").IgnoreParameters().Return(false); ILogger l = mockRepository.DynamicMock<ILogger>(); SomeClass someClass = new SomeClass(s, l); mockRepository.ReplayAll(); someClass.SomeMethod("What you put here doesnt really matter because the stub will always return false"); l.AssertWasCalled(l => l.Log("Invalid string")); }