I’ve worked in code where isolated tests with mocks get in the way of refactoring.

This points to a design problem, which, when fixed, makes the “mocks problem” disappear.

Almost always this involves:

  • inverting a dependency;
  • depending on a smaller part of a collaborator;
  • collapsing an obsolete layer.

What many people call “tightly-coupled to the implementation”, I interpret as “revealing unnecessarily complicated dependencies”.

Seeing it that way helps me fix the problem.

Listen to the tests; they’re telling you something.

When you don’t listen to the tests, you propagate decision problems throughout the system, and when you have comprehensive tests, sadly, you make the problem hurt more.

Wait—that’s good!

Most people find it harder to tolerate a more painful problem.

They feel compelled to do something about it.

Unfortunately, too many programmers blame the tests rather than the production code design.

I understand.

When we hold our nose while copying and pasting code, it hurts the system, whether we’re wildly duplication production code or test code.

Same result.

J. B. Rainsberger, comenting in favor of mocks on James Shore’s How Does TDD Affect Design?

One of the adjustments I had to make when I dove back into startups was realizing that Defects Zero, with its focus on throughput, doesn’t make sense for projects on the runway, when latency is key. On the runway, the key is how many questions can you ask and answer, how many assumptions can you validate per unit time (and dollar).

(…)

I don’t like working with defects piling up, but the economics of my current situation are that for the moment validated assumptions are worth a lot and reliable software is only worth a little.

(…)

In an environment of great uncertainty and minimal resources, when latency is key and the half-life of code is short, that means testing less and carrying defects.

The first defect was clear–projects that were closed caused an exception. Writing the test was easy–clone an existing test but close the project before running Max. Sure enough, red bar. A two-line fix later, green bar.

The second defect posed a dilemma. I could see how to fix the problem, but I estimated it would take me several hours to learn what was necessary to write an automated test. My solution: fix it and ship it. No test.

I stand behind both decisions. In both cases I maximized the number of validated experiments I could perform. The test for the first defect prevented regressions, added to my confidence, and supported future development. Not writing the test for the second defect gave me time to try a new feature.

As to your real question, the idea of immediate compilation and “unit tests” appeals to me only rarely, when I’m feeling my way in a totally unknown environment and need feedback about what works and what doesn’t.

Otherwise, lots of time is wasted on activities that I simply never need to perform or even think about.

Nothing needs to be “mocked up.”

When I want to rescue legacy code, I reach for Mockito. When I want to design for new features, I reach for JMock.

By default, JMock assumes that a test double (a “mock”) expects clients not to invoke anything at any time.

(…)

On the other hand, Mockito assumes that a test double (sadly, also a “mock”) allows clients to invoke anything at any time.

(…)

When I work with legacy code, I mostly write learning tests to discover how different parts of that legacy code behaves. Usually legacy code has obscene and overwhelming levels of interdependency, and Mockito helps me manage that, by allowing me to worry about one crazy dependency at a time.

When I design for new features, I mostly write design tests that describe the new behavior I want to implement. With the nice green field of a new interface, I need JMock to encourage me to clarify the interaction I need. Whenever my production code attempts to use a collaborator, JMock effectively reminds me to ensure that I want that interaction. Most importantly, JMock stops me from introducing dependencies that I don’t need.

(…)

Mockito helps me tolerate high accidental complexity while I work to reduce it.

JMock tries its best to stop me from introducing accidental complexity.