What's the purpose of Mocking?Mocking is extremely common in unit tests, but why? The basic idea is that you are attempting to test one specific "unit" of functionality, but you don't want calls to the database, third-party calls, or other side-effects creeping into your unit tests. On the surface this seems like a valid use-case.
Why is Mocking bad?There are many problems related to mocking that I'll try to quickly step through.
Unnecessary Complexity and Decreased ModularityThough the purpose of mocking is actually to reduce complexity it very often increases the complexity of your unit tests because you often end up with confusing and complicated mocking to avoid running aspects of your code that you don't want executed in your tests. You often end up pulling your hair out trying to figure out how the internals of the system are supposed to work in order to get the mocks to operate as expected and then all your tests start breaking as soon as that internal logic changes. You often end up in a scenario where your code works fine because it's written modularly to continue working but your unit tests break because they are essentially implementing the internal functionality via mocks.
Unit tests testing the Mocking frameworkIn extremely complex unit tests or even simpler unit tests that are managed over a long time or through many developers it becomes more and more confusing what the purpose of the test actually is. I can cite dozens of examples where I've been auditing unit tests only to find out that the unit test does nothing but test the mocking framework instead of the code.
Need for Mocking Represents Bad Code SeparationThis is what my entire complaint really boils down to. If you actually need mocking in your unit tests, your actual code that you're testing is poorly written in the first place. You should be writing code in proper units to allow individual testing without the need for mocking at all.
You might have a scenario similar to the code above and you want to test the checkout functionality of your system, but you don't want the payment gateway to be called and you don't want it actually persisting to the database so you might mock those out to avoid those being called. Instead, consider the idea that there are four units of functionality clearly defined in this case:
- Validating the Order
- Calling the Payment gateway
- Persisting the information to the database
- Creating a Receipt
You can see now that each unit of functionality has been broken out into its own method that can be individually tested as needed without the necessity of mocking. Further, the code is significantly cleaner and more maintainable as a result of this refactoring. Not only does it make our unit tests cleaner and easier to maintain without mocks, but it also enforces a higher level of code quality through modularity in your code than was there before.
If you ever decided that the checkout method needed to be unit tested you could refactor it so it takes in a PaymentGateway implementation and Persistance implementation as arguments to avoid side-effects and allowing a test instance to be created to fulfill those needs. Though this is a form of mocking, it follows a better paradigm of consistency and modularity than mocking frameworks do.
As you can see, this is an all-around better way to write code and though it is understood that in existing projects mocking still may be necessary, there is no excuse for such to be necessary in new code.