7

From Wikipedia (emphasis mine, internal references removed):

In the book "The Art of Unit Testing" mocks are described as a fake object that helps decide whether a test failed or passed by verifying whether an interaction with an object occurred.

It seems to me that mocks are testing implementation. Specifically, they test that the way that the implementation interacted with a particular object.

Am I interpreting this correctly? Are mocks an intentional breaking of the "test the interface, not the implementation" mantra? Or, are mocks testing at a level other than unit tests?

2 Answers2

6

Correct, mocks do not follow the classicist mantra of "test the interface, not the implementation". Instead of state verification, mocks use behavior verification.

From http://martinfowler.com/articles/mocksArentStubs.html:

Mocks use behavior verification.

Mockist tests are thus more coupled to the implementation of a method. Changing the nature of calls to collaborators usually cause a mockist test to break.

This coupling leads to a couple of concerns. The most important one is the effect on Test Driven Development. With mockist testing, writing the test makes you think about the implementation of the behavior - indeed mockist testers see this as an advantage. Classicists, however, think that it's important to only think about what happens from the external interface and to leave all consideration of implementation until after you're done writing the test.

Community
  • 1
  • 1
2

It seems to me that mocks are testing implementation. Specifically, they test that the way that the implementation interacted with a particular object.

100% correct. However, this is still Unit testing, just from a different perspective. Let's say you have a method that's supposed to perform a function on 2 numbers using some kind of MathsService. MathsService is given to a Calculator class in order to do the math for the calculator.

Let's pretend MathsService has a single method, PerformFunction(int x, int y) that is supposed to just return x+y.

Testing like this: (all below = pseudo code, with certain bits left out for clarity)

var svc = new MathsService();
var sut = new Calculator(svc);
int expected = 3;
int actual = sut.Calculate(1,2);
Assert.AreEqual(expected,actual,"Doh!");

That's a black box test of the unit Calculator.Calculate(). Your test doesn't know or care how the answer was arrived at. It's important because it gives you a certain level of confidence that your test works correctly.

However, consider this implementation of Calculator.Calculate:

public int Calculate()
{
    return 4;
}

Testing like this:

var svc = new Mock<IMathsService>(); //did I mention there was an interface? There's an interface...
svc.Setup(s=>PerformCalculation(1,2)).Returns(3);
var sut new Calculator(svc.Object);
sut.Calculate(1,2);
svc.Verify(s=>PerformCalculation(1,2),Times.Once,"Calculate() didn't invoke PerformFunction");

This white box test doesn't tell you anything about the correctness of the PerformFunction method, but it does prove that, regardless of the result, the Calculator did pass x and y to the IAdditionService.PerformCalculation method, which is what you want it to do.

You can of course write other tests to verify that the result of the PerformCalculation test is passed back without modification to the caller, etc.

Armed with this knowledge, if your first unit test now fails, you can, with a high degree of confidence, jump right into the MathService class to look for problems because you know that the issue is likely not the Calculator.Calculate method.

Hope that helps...

Stephen Byrne
  • 7,400
  • 1
  • 31
  • 51