Reader Question: "Why did you say to avoid mocking?" ✉️
Jan 13, 2023 3:01 pm
Last week I wrote about operational excellence and I made a comment that resulted in questions about my stance against using Mocking frameworks. So, let's take a look.
Before I go into it, I want to say that if you're highly skilled with mocking, it can be an effective way to go. However, in my experience, that level of experience doesn't exist often enough where I'd bet on it.
Now, mocking frameworks provide an interesting utility in that they are able to stand in-between real code and intercept messages. This ability to use those frameworks to break dependencies is the biggest benefit people cite, and it also creates its weakness.
In order to mock something using one of these frameworks you have to create the mock version of it and either tell the framework how it should handle various invocations, or you tell the mock framework to verify specific invocations were called. Again, this sounds fine, but there is a tradeoff here.
That tradeoff is that in our pursuit to test Class A we've now used a mocking framework to test how Class A was implemented through responses and verifications. So instead of our tests proving that the contract of a method in Class A is valid, we've instead tested that the implementation of that method contains the right calls on the mocked dependency and assume that this is equivalent to things working.
Second, mocking frameworks artificially break apart dependencies which often causes a shadow of it in the testing framework. It is incredibly easy to have tests with mocks pass whose actual code fails. The reason is that instead of testing code, you're now testing your mocks and your tests are only as good as your mocks. I'm sure you've seen plenty of mock statements that never pin down method parameters or realistic responses. That sort of laziness in the mock can only really tell you the code ran, not that it ever worked.
So, let's contrast this a bit to not using mocking frameworks. A few things will become immediately obvious. First, it'll be obvious that Class A has a lot of hard dependencies. In order to test Class A you have to let those dependencies exist because by design Class A required them to function. This sounds clumsy, but that is only because of how the code is actually designed. Mocking gives you tools to hide that bad design.
Now, someone will say something about the problems with integration tests, but I'll say that the line where things integrate is almost always the most fragile and at the end of the day I want to be sure they work, so I'll let tests run there. I use design to continually isolate those integration points, but I will test them without mocking. When those tests pass they are real, valid, and consistent. Mocking, by contrast can only ever tell you that your mocks were used and not that anything worked.
This is, at the end of the day, a contentious topic, but what I've seen time and time again with mocked codebases is that the tests pass in a code base riddled with problems and it is never obvious why. When I test without mocks, it's painfully obvious where the problems are and the pain is immediate. The easier it is for me to test without mocks the better designed my code is and the more stable it is.
If you'd like to read a bit more, Martin Fowler's blog has a great article about mocking, stubs, etc.