Agreed with the articles, mocks are very tied to implementation details. I almost always prefer state-based testing:<p><a href="https://martinfowler.com/articles/mocksArentStubs.html" rel="nofollow">https://martinfowler.com/articles/mocksArentStubs.html</a><p>As "observable state goes from a to b" is much closer to a business/functional requirement that will still/always be true, regardless of refactorings.<p>Refactoring in codebases with state-based tests is a pleasure; in codebases with mock-based tests it's tedious, constantly updating tests when no semantic behavior was supposed to change.<p>Also, mocking via module hacks like in the article (and in the JS world) is scary; modules are basically global variables so it's a very coarse grained slice point. Dependency injection is almost always better.
I really don't like that the topic title is a general statement but the article under is talking strictly in python landscape as an example and not discussing the idea itself.<p>Mocking by itself works fine. It's a good idea that works if used correctly. Misusing it or bad usage leads to issues - duh.<p>I'm familiar with the narrative that anything that is too hard (to get right at the first try) in our field means that it is bad - but I don't agree with it at all. We are at the point where craftsmanship should be a good metric to distinguish medicore developers from experts..
For a person who does not code very large programs in Python, this looks scary and like something that is hardly "only one way to do it" and that the most elegant solution gives you what you want. How I import things (or the libraries I depend on!) affects how I can write my tests? Really?<p>My own Python scripts are typically single-screen in length. And one-off stuff, where they either work or don't, basically.<p>Is this stuff really what developers of large-scale Python programs have to take into account? Or is this blog post misinformed, because there is an obviously better and standard way?
If you find yourself fighting with mocks, ask yourself: is there a deeper design problem with my code? I often find that things I can't test easily have crappy design.
I think Ned strikes the right balance, showing risks associated with mock objects without condemning them outright. There is no doubt that people sometimes go overboard with mocking and there is no doubt that there are situations where it is really helpful.
The author hasn't a problem with mocks. It has got a problem with monkey patching. You can use a mock along dependency injection, and never run into the problems the author has.
Mocking is hard <i>really</i> hard, in order to mock something you need to imitate its functionality and interface. This means the mock is inherently tightly-coupled to the implementation, which is now another dependency in your system.<p>After trying to work with mock databases and file systems, I've personally found that there's no substitute for the real thing. There's much less maintenance and greater reliability in spinning up a test environment with the exact implementation that will be used in the production environment.<p>There are cases where mocks are the only practical solution, (embedded systems, distributed systems) but mocking is surely the last resort...
Python core developer Lisa Roach has a nice how-to video on exactly this subject: <a href="https://www.youtube.com/watch?v=ww1UsGZV8fQ" rel="nofollow">https://www.youtube.com/watch?v=ww1UsGZV8fQ</a>
I've wrestled with this problem several times. My conclusion was mocks are just fine, but this is a wart in that there isn't "one way to do it"<p>For the most part I can get by with one rule: always mock <i></i>the module<i></i>.<p><pre><code> with mock.patch('os.listdir'):
</code></pre>
will always work, even if it doesn't accomplish what you want.<p><pre><code> with mock.patch('mymodule.os.listdir')
</code></pre>
will fail if that module does not explicitly <i>import os</i> and instead does something like <i>from os import listdir</i> (perhaps because a later dev did not realize importing os directly was actually a requirement for the test and changed the code).<p>The rule is not perfect though. In the above case, the error will actually be<p><pre><code> ImportError: No module named os
</code></pre>
This can be fixed with e.g.,<p><pre><code> assert hasattr(module, 'os'), "os module is not explicitly imported"
</code></pre>
as a preamble to your test but ... it is not perfect by any means.<p>EDIT: formatting
I've always been dubious of using mock for tests which involve external API calls; at best they require you to reimplement the API according to the documentation (which there may be none, or that the API doesn't follow exactly for edge cases (i.e. the things you're meant to be testing)). At worst you're implementing a very small subset of the behaviour of the API and not testing how your code responds to the other behaviour. But I haven't come across other solutions (not that I have that a heap of experience here, just contributions to a couple of oss projects).
“...there are other approaches to solving the problems of isolating your product code from problematic dependencies.”<p>What are some other approaches?<p>I have followed the redux-saga pattern with success but how else should we accomplish the same goal?
Ned's implicit definition of a mock is narrower than the generally accepted one. He actually described a stub created by monkey-patching. A mock allows for call verifications as well.<p>There are 3 main categories of techniques for managing dependent components used these days:<p>1. In-process class/method/function mocks or stubs (<a href="http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html" rel="nofollow">http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dum...</a> and <a href="https://martinfowler.com/articles/mocksArentStubs.html" rel="nofollow">https://martinfowler.com/articles/mocksArentStubs.html</a>)<p>1a. By monkey patching (which is what Ned has demonstrated in his article very well)<p>2a. By dependency injection<p>2. Over-the-wire API mocks or stubs (<a href="https://en.wikipedia.org/wiki/Comparison_of_API_simulation_tools" rel="nofollow">https://en.wikipedia.org/wiki/Comparison_of_API_simulation_t...</a>)<p>3. Virtual services/simulators
(<a href="https://en.wikipedia.org/wiki/Comparison_of_API_simulation_tools" rel="nofollow">https://en.wikipedia.org/wiki/Comparison_of_API_simulation_t...</a>)<p>It's worth keeping in mind that all of them are part of a wider group of test doubles: <a href="https://www.infoq.com/articles/stubbing-mocking-service-virtualization-differences/" rel="nofollow">https://www.infoq.com/articles/stubbing-mocking-service-virt...</a><p>Other options available for decoupling from test dependencies:<p>1. In-memory database <a href="https://en.wikipedia.org/wiki/In-memory_database" rel="nofollow">https://en.wikipedia.org/wiki/In-memory_database</a><p>2. Test container <a href="https://www.testcontainers.org/" rel="nofollow">https://www.testcontainers.org/</a><p>3. Legacy in a box <a href="https://www.thoughtworks.com/radar/techniques/legacy-in-a-box" rel="nofollow">https://www.thoughtworks.com/radar/techniques/legacy-in-a-bo...</a>
Beautiful explanation for something that tripped me up in early days of using mock/patch. Summary:
(1) Variables in Python are names that refer to values.
(2) (For that reason) Mock an object where it is used, not where it is defined.
the python mock module is one of those modules i would like to see rewritten from scratch. you won't get it right by first principles. you always need to go to the documentation. that's a sign that something is not right imo.