When to use a Mock v. Stub, or neither?

2k views Asked by At

I've been reading up on Mocks and Stubs, their differences and uses. I'm still a bit confused, but I think I've got the jist of it.

Now I'm wondering about applications. I can see the use in creating "fake" objects in testing scenarios where the actual objects are too complicated to haul around to test one aspect.

But let's consider my application: I'm working on a computational geometry library. Our library defines points, lines, linesegments, vectors, polygons, and polyhedra, along with a bunch of other objects and all the usual geometric operations. Any given object is stored as a list of points or directions, or lower level objects. But none of these objects takes more than a few milliseconds to generate.

When I'm testing this library, does it make sense to use Mocks/Stubs anywhere?

Right now we just use particular test cases. We're calling them stubs, but I don't think they meet the technical definition of a stub. What do you think better vocab for that would be? "TestCases"? "Examples"?

SourceCode: https://bitbucket.org/Clearspan/geometry-class-library/src

Edit: Note that we're striving for immutability in all our geometry objects, so it only makes sense to test the results of operations, but not state changes to the initial objects.

2

There are 2 answers

0
k.m On BEST ANSWER

The fundamental difference between mock and stub is that mock can make your test fail. Stub can't. Stub is used to guarantee correct program flow. It is never part of assert.

Note that mock can also be used to guarantee flow. In other words, every mock is also a stub, and stub is never a mock. Because of such overlapping responsibilities nowadays you don't see much distinction between mock and stub and framework designers go for more general terms (like fake, substitute or catch-all mock).

This realization (mock - assert, stub - flow) helps us narrow down some usage scenarios. To start with the easier one...

Mock

As I mentioned mocks are used in asserts. When the expected behavior of your component is that it should talk to this other component - use mock. All those

emailSender.SendEmail(email);
endOfDayRunner.Run();
jobScheduler.ScheduleJob(jobDetails);

can be only tested by asking "Did it call ScheduleJob with such and such parameters?" This is where you go for mock. Usually this will be mock's only usage scenario.

Stub

With stub it's a bit different. Whether to use stub or not is a design question. Once you follow regular loosely coupled, dependency injection-based design, eventually you will end up with a lot of interfaces.

Now, when in test, how do return value from interface? You either stub it or use real implementation. Each approach has its pros and cons:

  • with library-generated stubs, your tests will be less brittle but might require more up-front work (setting up stub and such)
  • with real implementations, setup work is already done but when Angle class changes CoordinateSystem might fail... Is such behavior desirable or not?

Is it? Which one to use? Both! It all depends on...

Unit of work

We arrived at final and the actual part of the problem. What is the scope of your unit test? What is the unit? Can CoordinateSystem be detached from its inner workings and dependencies (Angle, Point, Line) and can they be stubbed? Or more importantly, should they be?

You always need to identify what your unit is. Is it CoordinateSystem alone or perhaps Angle, Line and Point play important part of it? In many, many cases, the unit will be formed by both method and its surrounding ecosystem, including domain objects, helper classes, extensions or sometimes even other methods and other classes.

Naturally, you can separate them and stub all the way around but then... is it really your unit?

0
bateloche On

As a rule of thumb, use Mocks when you need to simulate behavior, and stubs when the only thing that matters in your test is the state of the object you're communicating with.

Taking into consideration the edit you made to your post, when you need to receive an immutable object use a stub, but when you need to call operations that object exposes then go for a mock, this way you are not prone to failing tests due to errors in another class implementation.