Mock objects and Testing
March 15th, 2009
This has been a much discussed topic over the web in the last couple of years. I was responding to one of the emails at work about mock objects. While organizing my thoughts about Mock objects, I decided to write this post.
Various thought leaders in TDD promote Mock objects. I agree with some of it but not everything they say.
Some caution towards using Mocks extensively:
-
Using Mock objects might affect design decisions:
-
The mocked classes (and the methods used to set up expectations) cannot be final. If called, their normal code may be executed. I prefer making every class either final or abstract unless there is a strong reason to not to do so.
-
A class with only private constructors cannot be instantiated.
-
The behavior for equals() and hashCode() when using mocks may not be what you normally expect.
-
Private methods cannot be mocked. I prefer making every method private unless there is a strong reason to not to do so.
-
-
Trustworthiness & ROI of Mocks: Mocks may hide integration problems and push the burden on other tests. High-level and End-to-end tests give the most ROI, in my opinion. I would value these over the unit tests that use mocks extensively when constrained by Time & Resources. Most of the times, I have seen that tests using mocks extensively completely miss the intent of the test..!
-
Increased complexity and maintenance costs - Setting up mock object expectations create clutter and duplication to test code. Mocking Fine-Grained or Chatty Interfaces results in a lot of brittle unit-test code. High-level tests may not require such extensive changes.
-
It is a well-known fact that unit tests (that use mocks extensively) don’t catch as many bugs as the integration/high-level tests.
I do not want leave an impression that I do not like the idea of using Mock objects for testing. We just need to use it for the right things and right places after understanding it’s rewards and risks, just like any other tool/framework.
Mocking is still suitable for various scenarios that include:
-
Testing sub-systems that don’t exist yet or are being developed separately. But, then after the dependent code is available, I would switch back to real code from mock objects.
-
Legacy code that does not have interfaces and is tightly coupled.
-
Sometimes we may need to mock only some methods of a class and keep the normal behavior of others.
-
Testing against slow/heavyweight components that have significant dependencies.
-
To simplify Unit testing when there are no alternative ways to test the same effectively (for example, if the dependent component is very hard to setup/configure, etc.).
IMO, choosing the right scenarios objects (if any) for using Mock objects is the trickiest part.
I personally avoid using Mock Objects (unless there are no other easy options) by using good OO Design & Programming practices to write my code such that it is testable without having to use Mock Objects.
Leave a Reply