Hello Comalatech

Mi primer encuentro con David Bonilla tuvo lugar cuando terminaba 2011. En aquel momento yo estaba en pleno desarrollo de la herramienta Karmacracy y David era un gran embajador de la misma. En ese…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




The hidden costs of mock injection

When I first started using Mockito, I was amazed at how it manipulates objects for tests. I used annotations to declare mock fields and then either use initMock on the setup method or a MockitoRule, as it was what most online tutorials explain to do.

Both these techniques use reflexion to find and instantiate fields in the test class marked with Mockito related annotations. In fact, MockitoJUnit internally uses MockitoJUnitRule which in turn uses initMocks.

However, there is a couple of things to consider next time you write or update a test with Mockito. I’m gonna describe how using Mockito manually may be beneficial.

As mentioned, the way Mockito injects test doubles is reflexion. It can be slow. I’ve seen up to a 10x slow down on unit tests. Integration or instrumented tests don’t have this effect because they have other time-intensive constraints.

When doing unit tests, rapid feedback is a must have. Especially with techniques like TDD. Even 300ms per test suit can add up to a considerate amount of time. When committing code, it’s always a good practice to run all unit tests and with 100 test classes adds up to 30 seconds.

So, what’s the alternative? We can instantiate test doubles using Mockito’s factory method mock().

If you use Kotlin (tests are a great place to start) then it can be simplified using inlined functions with reified types.

When declaring doubles with @Mock, we have to leave them as non-final. It’s a good practice to treat test code in the same way as we treat production code. I have said this in the past, but now it gives me shivers down my spine when I hear: “Well, it’s just a test”.

Fields that don’t change during the lifecycle of a class ought to be final. It gives the contract that it’s gonna remain pointing to the same object and avoids unexpected changes. So, why don’t we do this in our tests?

If we use declarative doubles, they can be final.

Ensuring the field is gonna always point to the desired double. JUnit creates a new instance of the test class for each test, so the behaviour of each of the doubles will reset after each run.

When not using a variable, most IDEs and static analysis tools warn you about it. However, when using field injection, the IDE doesn’t know that JUnit instantiates the object later on. To avoid false positives some editors, like it’s the case for IntelliJ, suppresses warnings on variables with specific annotations.

The results are that visibility for unused fields gets lost. If someone comes after to update a test, there is a significant danger that, if they aren’t extra careful, they will leave a field that is no longer needed. I’ve done it, and I’ve seen good developers do it.

Note: The newest versions of IntelliJ are a bit more clever about this.

Continues Integration servers that integrate with static analysis tools have the opposite issue. Because they aren’t instantiated the SA tool will report that those fields are not used. And many projects decide to disable analysis of test code. Not a wise decision IMO.

The area where we declare the mocks tends to become a dumping field where every test drops its needs. Adding extra unnecessary complexity and making it hard to follow any non-trivial test.

Well written tests should be independent of each other. As a test grows in size, we start dumping required doubles due to the need to be injected by the test rule.

What I’ve done in the past to solve this issue is to create local factory methods which instantiate the subject of the test.

Since this is an implementation detail, we can move it to the bottom of the file, after our tests.

Now, in our test, we can use this “given” method.

Let’s look at an example where the service consumes an ApiClient which in turn produces some data that we want the service to manipulate.

For a different case we can define a new factory method.

By encapsulating the expectations inside “given methods” we can scale test suites easily and without potentially influencing other tests in the process.

If tests start to overlap expectations (usually with large objects), it may be interesting to create an internal builder that provides them so we can chain them.

However, this should be a last resort, and its need may indicate that the tested object has too many dependencies.

If we use these expectation methods, there is no need to have a setup method (or a tear down). Like an abstract class that defines behaviour which the extender has to be aware of. With a setup method, we are injecting logic to all of our tests, even if they don’t need it.

Mockito’s factory method mock() creates a proxy object that records interactions. If we are creating an object that merely provides behaviour or data and we don’t care for verification, we can make the doubles a bit more efficient.

By default, Mockito’s instances record every interaction so that we can use verify after we exercise an object. However, this can be expensive, and we should only use it as a last resort. Using verify can indicate that we are doing too much white box testing and the test cares too much about the implementation. Asserting a final state makes the code that we are testing more open for modifications.

We can create a stub with Mockito.

For convenience I recommend to have a static factory method somewhere like a helper called MockitoExtensions, for example.

Or the homologous inlined function in Kotlin.

The positive side effect of using this by default is that it will fail if we try to verify, and at that point we can asses whether we require to record interactions with a mock instead of a stub.

Add a comment

Related posts:

The Whole Nine Yards of Neural Control

Anyone who has lost a limb has for generations had to make do with either a purely cosmetic prosthesis or one which could only perform the most basic functions. In recent years, bionic limbs have…

Every Girl has heard these lines from boys!

So actually when we were young love used to happen like in matric. So for the viewers who are not from Pakistan they must be thinking that was is matric?. It is actually tenth standard. So when I was…

Nemesis Service Suite Download

Nemesis Service Suite Download V1.0.38.15 is a small windows application which allows Nokia users to unlock all type of screen locks including Pattern, Passwords, Face locks, Finger-print locks, and…