Reputation: 93
If I use a mockito mock of an object being injected into the SUT as an argument, what happens if during refactoring the code is re-organized to call another non-mocked method of that same mock? My tests would fail and I'd have to go back and change my tests and set them up for this new call (the opposite of what I'd want to be doing when refactoring code)
If this is a common occurrence during refactoring, how can using mocks be of any use except for when mocking external, resource-intensive entities (network, db, etc.)?
I'm using mocks to mock out objects that would take hours to set up given my team seems to love monstrously deep aggregate objects.
Upvotes: 1
Views: 643
Reputation: 95614
You are correct that refactoring will likely break code that depends on mocks. Mockito doesn't know that method foo(int start, int end)
and foo(int start)
accomplish the same thing, and if you switch between them while refactoring, your Mockito mocks will very likely break. Mockito does provide reasonable defaults for unstubbed calls, like 0, null, or an empty list; however, many refactors will need more-realistic values.
In general, I've heard the tendency of a test or test fixture to fail when the system is behaving correctly called its "brittleness".
Part of this is derived by the choice of mocking framework: Mockito started life as a fork of EasyMock, where EasyMock will fail by default if there are either too many or too few calls, but Mockito will ignore unexpected calls and otherwise provide "nice" default behavior. The other part is determined by how you use the framework, where verifying unnecessary details (unimportant calls or parameters) may make mocks more brittle than they have to be.
Things Mockito is good at mocking:
Things Mockito is not good for mocking:
final
start to sneak in. This might be a good reason to create a wrapper you do control.It's worth saying that no test double will be perfectly safe; your system may cache, combine, delay, modify, or otherwise adjust interactions with its collaborators, and all of that can break pretty much any test double you write. The art of writing a flexible test is to rely on as few implementation details as possible, balancing the risk of brittleness against the requirement to thoroughly test the system and its interactions with the outside world.
All of that said, to directly answer the question "how can using mocks be of any use", see JB Nizet's great analogy here: If you're trying to create a bomb detonator, you probably want to test it, but the cost of using the real thing is simply too great. The difficulty involved (and the optimal choice of test double) will vary based on whether the bomb in question has a hundred little triggers, or a single boom()
method.
For more details about test doubles (a category that includes mocks, fakes, and dummy objects) and their pros and cons, see Martin Fowler's article "Mocks aren't Stubs".
Upvotes: 1
Reputation: 1270
I would suggest that you only mock the bare minimum of what's needed. Mockito has many facilities for doing this (spies, ability to return specific data/mocks when a method is invoked a certain way, etc.) but it ultimately comes down to having testable "seams" in your code. If you haven't already, I would recommend reading Michael Feather's book Working Effectively with Legacy Code for many suggestions on how to do this.
Upvotes: 0