Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Mocks don't cause coupling. Programmers do.


Cute, but even as terse as this comment is its sentiment is incorrect.

Mocks, almost by their very nature, encourage coupling. Ask yourself the question "What tends to get mocked? " Answer: Objects which are difficult and/or heavy to instantiate, but which are outside of your direct control and cannot be changed, e.g.: database connections, network sockets, etc.

So when you want to write tests around this you are almost forced to mock them out. So you instantiate those classes (as mocks) in your test class, so that the code under test. There are now two references to that class: one in the test, and one in the code being tested.

This is coupling. QED.


> Ask yourself the question "What tends to get mocked?"

Answer: every collaborator of the object under test, so that only the tested object is actually having its implementation exercised. All other objects that it needs to talk to — database connections, network sockets, or whatever — are replaced with mocks, and the test subject’s interactions with those mocks are verified, so that a) we have confidence that the tested object is interacting with its collaborators in the expected way, and b) the test itself doesn’t depend at all on the actual behaviour of those collaborators’ implementations, which behaviour should be explicitly exercised elsewhere.

That’s the opposite of coupling. I don’t understand where your answer comes from or what it means.


I view this as definitely enforcing coupling.

The issue is that you should want to make it very easy to change the API signature of internal components.

Mock objects can tend to reinforce internal coding choices when things that are not a public interface, or a service boundary, are mocked out.

If you're mocking at this level, there is a potential for refactoring of intra-class API contracts to become much harder to change.

Thus, it would be better IMHO to mock out only meaningful service boundaries, and concentrate testing at the public contracts.

You can still get very strong coverage, but you're just thinking about a different level of inputs.


The point more accurately is that coupling to the interface used to communicate with your collaborator I.e. If you were to refactor the way that the test object communicates with it's collaborator (without changing it's external behaviour) you are now forced to update the mocks. If the collaborator is only used within your test object and nowhere else in your system the communication between them doesn't need to be pinned down, the test should just test both of them at the same time. This is the difference between a white box test (where your mocking has to expose implementation details) and blackbox testing (where you can refactor without breaking tests).


Actually, my answer to "What tends to get mocked?" is quite different from yours. My answer is "Everything that is not what is currently being tested." So if I'm testing method Foo.bar, all of the method calls that leave Foo.bar are mocked. If Foo.bar calls A.b(), A will be mocked as its functionality is not currently under test. The only real exception is if A.b() is a trivial implementation and I don't already have A mocked for some other reason. If A.b() is a setter/getter, for example, I might not mock it.


Mocking "everything that is not what is currently being tested" is tautological.

Mocking and stunning in the manner you describe makes tests extremely dependent upon the current implementation. In fact, it inverts the relationship your tests should have with your development process.

Tests should allow you to refactor without breaking tests, and tests should break when APIs you call change in incompatible ways. These are the two primary long-term benefits that tests offer. Stubbing and mocking all of their internals causes tests to break when you change internal implementation (library Foo was switched to library Bar, but Foo is mocked and has a different API than Bar). It also causes tests not to break when the Foo API is incompatibly changed by another developer.


Tests are generally for making sure your code appropriately handles cases where Foo behaves a certain way. That could be returning a certain value or throwing an exception, whatever types of branches you might have. Foo's implementation doesn't matter, therefore it gets mocked. If Bar doesn't, for example, throw the same exceptions, you've changed your method behavior and it makes sense to also update your tests/mocks.


Do you always hit the real database and network when you test? Because what you describe would also rule out stubs and fakes.


Assume that when you have N consumers of an interface then your coupling is N. Mocks consume an interface as does your codebase so your coupling is at least N=2 whereas your codebase alone was N=1. By using mocks, programmers cause coupling to increase so programmers should stop using mocks to decrease coupling.


This is technically true but I don't think it's a good argument against mocking or test doubles.

By this reasoning, the test suite itself causes coupling to increase. For any component C, you have at least N=2: when it's used in production code, and when it's tested. If you don't test C, you reduce N to 1. This doesn't sound like a good argument against testing, though.


> Mocks consume an interface as does your codebase so your coupling is at least N=2 whereas your codebase alone was N=1.

I really hope that you're not being serious here.


100%. Its called efferent coupling.


It's answer which is technically 100% right; and practically 100% missing the point. So it seemed facetious.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: