Hacker News new | ask | show | jobs
by matt7340 1965 days ago
Classic article, and important distinction. I’ve found that those who aren’t familiar with the distinction tend to be less aware of what and how they’re testing. Lack of awareness or understanding tends to lead to thing like excessive mocking.

Poorly named libs (e.g. mockito) just exacerbate this problem.

1 comments

Hmm, I'll bite. What exactly in your opinion makes the difference here with awareness or lack thereof (of the distinction)?

The way I see it mocks are the all encompassing tool here. With mocks you can both stub behaviors and, if you want, verify behaviors.

And in reality every mock needs to act as a stub, e.g. a mocked method returning a preset value. So, it seems natural that "mocking" as more encompassing concept is what is used in library names. If people end up using those to mostly create stubs instead of mocks so what?

My experience so far has been that people naturally flock to a solution for testing that is flexible and lets them quickly cover the boundaries of the code they are testing. If people want to check if their code calls a method x times but are not interested in what that method does for the test, they use mocks. If they do they use "the real thing" or a fake on which they can validate state later. And if they just need a method to return something and don't care about whether it's called or how often they use a stub.

I feel that often when you stub you can also go ahead and add code to verify some behaviors (mock) to get some extra coverage. I don't know I just don't think it's bad to mix and match if it gets you to more things tested.

My experience with junior (and sometimes not junior) developers has been that the lack of understanding leads to confusion about what they're trying to test. I think the ambiguity leads to a tendency toward mocks, which leads to tests that are focused on collaboration and implementation rather than output. Sometimes that makes perfect sense, but I've also seen where it encouraged more tests that are too implementation reliant.

Nothing wrong with mocks of course, sometimes they are absolutely required. I just favor isolation using stubs, and using mocks sparingly and only when absolutely needed.

> ...in reality every mock needs to act as a stub I disagree. In fact a collaborator method call that doesn't return a value is a great candidate for mocking. Conversely, a collaborator that does return a value can usually be stubbed.

> ...when you stub you can also go ahead and add code to verify some behaviors (mock) to get some extra coverage Entirely dependent on context of course, but to generalize, this feels risky. In many cases adding extra coverage via mocks might be making tests more brittle, for little to no value gain. Further if every method you mock is returning a preset value, there's no reason to verify expectations on it, because it's a stub that will allow verifying expectations on the output of the object/function under test.

As far as library naming, it's just a pet peeve stemming from "mock" somehow becoming synonymous with "test double". If people use a "mocking" library for stubs... well... that's good! :)

In my experience, I have never seen people accidentally (or ignorantly) wire a stub to a stub. When you do this it is really obvious, because there are only trivial, or maybe no, assertions possible.

I have often - like on a team of 5-10 at least 2x per year - seen people wire a mock to a mock or a fake to a mock, and either not realize it (because it's under six layers of DI) or just they're not paying attention but someone said they "have to write tests" and trying to get it over with. And this is generally a lot less obvious, because the test suite is still full of assertions, sometimes even totally reasonable-looking ones. (Such tests are even sometimes "real tests" - but that the mock is doing what's expected, not of the desired SUT.)

There's also the general argument from the principle of least power. The less code in your doubles, the more certain you are the doubles are doing what you expect, and the more you're verifying what really matters in your code. Like how pure functions are preferable to impure functions when possible, having comprehensive tests without concerns about "side effects" also means your code has fewer relevant side effects.