As I do more and more work with the exercism.io community, I've noticed a reoccurring theme. There seems to be a lot of confusion around the various types of "test doubles" and what each of them actually do.
Now, if you've had some exposure to test driven development already, you may already be familiar with test doubles such as fakes, stubs, spies, mocks, and dummies (did I miss any)? I feel like a lot of these terms are very similar and do not add much value, but rather just further complicate the discussion.
I prefer to simplify by classifying every test double as either a Fake, Stub, or Mock. I would even categorize a test double as a Fake. But what are Fakes, Mocks, and Stubs? I've taken a leaf out of Roy Osherove's book The Art of Unit Testing to define them as I believe they are the easiest to understand.
Fake - A fake is a generic term which can be used to describe either a Stub or a Mock object. Whether it is a Stub or a Mock depends on the context in which it's used. So in other words, a Fake can be a Stub or a Mock.
Mock - A mock object is a fake object in the system that decides whether or not a unit test has passed or failed. A Mock starts out as a Fake until it is asserted against.
Stub - A stub is a controllable replacement for an existing dependency (or collaborator) in the system. By using a stub, you can test your code without dealing with the dependency directly. By default, a fake starts out as a stub.
Now that we have a formal definition of each, there's a few key points I want to highlight.
When you say mock, you probably mean stub.
This is probably the most common mistake I see when performing code reviews of test suites and discussing tests with other developers.
Consider the following code snippet:
var mockOrder = new MockOrder();
var sut = new Purchase(mockOrder);
sut.ValidateOrders();
Assert.True(sut.CanBeShipped);
This would be an example of Mock being used improperly. In this case, it is a stub. We're just passing in the Order as a means to be able to instantiate Purchase
(the system under test). The name MockOrder
is also very misleading because again, the order is not a mock.
A better approach would be
var fakeOrder = new FakeOrder();
var sut = new Purchase(fakeOrder);
sut.ValidateOrders();
Assert.True(sut.CanBeShipped);
By renaming the class to FakeOrder
, we've made the class a lot more generic, the class can be used as a mock or a stub. Whichever is better for the test case. In the above example, FakeOrder
is used as a stub. We're not using the FakeOrder
in any shape or form during the assert. We just passed it into the Purchase
class to satisfy the requirements of the constructor.
To use it as a Mock, we could do something like this
var fakeOrder = new FakeOrder();
var sut = new Purchase(fakeOrder);
sut.ValidateOrders();
Assert.True(fakeOrder.Validated);
In this case, we are checking a property on the Fake (asserting against it), so in the above code snippet the fakeOrder
is a Mock.
It's important to get this terminology correct. If you call your stubs "mocks", other developers are going to make false assumptions about your intent.
The main thing to remember about mocks versus stubs is that mocks are just like stubs, but you assert against the mock object, whereas you do not assert against a stub. Which means that only mocks can break your tests, not stubs.
When you say mocking framework, you probably mean isolation framework
In the same vein as "stubs are not mocks" I wanted to touch a little bit on the idea of mocking frameworks.
Unfortunately the term "Mocking framework", is confusing and ultimately incorrect. Take any framework you may consider a mocking framework (Moq, NSubstitute, etc), sure they can mock, but they can do so much more (e.g. they're also capable of stubbing).
The goal of these frameworks are to isolate your code. We should be calling them isolation frameworks. This even confused me for quite some time, having had some experience with Moq.
Consider the example
var mock = new Mock<IOrder>();
var purchase = new Purchase(mock.Object);
Assert.True(purchase.Pending);
Even though we called the variable mock
and Moq provides a class called Mock
, in this context, it's actually a stub.
So unfortunately, mock is an overloaded word. It's confusing. Avoid if it all possible, unless you actually are referring to mocking. Explaining to a developer that you can "isolate" something from its dependencies rather than "mock" its dependencies, feels more natural.
Key Takeaways
1) If you must roll your own class for isolation purposes, consider naming it a Fake. This gives you the freedom of using it as either a Mock or a Stub and does not give off the wrong intent.
2) Mocks are not Stubs. If you do not assert against it, it's a Stub.
3) Prefer the terminology Isolation framework. Mocking framework can be confusing.