I was working in a new service; I had been pushing commits as crazy, with their appropriate tests, and I was using kotlin, spring, jooq and mockito. Then, I had run into an issue that felt could be improved.
I had read about mockk from a coworker, so I gave it a try. I started to implement a new functionality that had nothing to do with another one and implemented the tests. The only relation they had was a table that the new functionality was reading from the database (not writing to, I promise!).
Guess what happened after that? Some other tests started to fail. Seriously, why fixing something opens several other bugs? How could be possible that a completely different test suite that had nothing to do with those services started to fail? The symptoms were really strange. I was looking at the logs,and I could see jooq retrieving a row and suddenly failing to map an object because the id field was null. As I said, that could not be possible since my DB access was readonly this time and tests were not doing anything related with db yet.
This was a really weird case, since running those tests alone worked but when invoked as part of the suite, failed. Moreover, altering the order of the tests make them work or fail, as it some dark magic was controlling the code.
Since JUnit 5 does not have yet a way to specify the order of the tests
much like JUnit 4 had, this was hard to test and identify.
Digging deeper I found that on the native call on
private native Parameter getParameters0(); was returning a different set of parameters after
ParameterAwareRecordMapper indeed uses
constructor.getParameters to map from a record to a POJO and
voila, after that call, jooq was unable to found any parameter from the record,
and thus populating a POJO with all fields initialized to
What jooq was doing was reading the parameter names from the constructor of the POJO and looking for fields with those names on the record. Since the POJO was generated by jooq itself based on the DB fields everything should work fine from here. It was not. This messed up my pipeline, my code and got me through the rabbit hole for at least a couple of hours.
I managed to isolate the problem in the least lines of code possible and so far
I have not found any work around nor way to reset what is happening after using
I suspect that the library uses reflection to inject its own constructor but forgets to keep the parameters name.
Here is the minimal test to reproduce it (remember that JUnit 5 can’t warrant test order so run them independently if
There are two tests, and a data class with two parameters in the constructor,
The first test shows that the class has only one constructor, but it is not the same after calling to
the second one shows that the parameter names are not present anymore.
One expects that just mocking an object does not mess with the loaded class by the JVM, but I got this:
15:25:12.631 [main] DEBUG io.mockk.impl.instantiation.AbstractMockFactory - Creating mockk for DataClass name=#2, moreInterfaces= 15:25:12.637 [main] DEBUG io.mockk.impl.stub.CommonClearer - Clearing  mocks java.lang.AssertionError: Assertion failed at org.tarodbofh.medium.mockk.MockkMessingNativeJVM.testConstructorEqualityAfterClearMocks(MockkMessingNativeJVM.kt:26)
This means that after calling
mockk() the constructor of the class had been altered!
Of course, the second test failed:
15:25:11.563 [main] DEBUG io.mockk.impl.instantiation.AbstractMockFactory - Creating mockk for DataClass name=#1, moreInterfaces= 15:25:12.555 [main] DEBUG io.mockk.impl.stub.CommonClearer - Clearing  mocks java.lang.AssertionError: [Extracted: name] Expecting: <["arg0", "arg1"]> to contain exactly in any order: <["name", "age"]> elements not found: <["name", "age"]> and elements not expected: <["arg0", "arg1"]> at org.tarodbofh.medium.mockk.MockkMessingNativeJVM.testConstructorGetParametersMocked(MockkMessingNativeJVM.kt:44)
The parameter names had changed, and that is why the other tests started to fail after I mocked an object on a Unit est. Don’t forget to compile with java parameters targeting Java 8 (see https://bugs.openjdk.java.net/browse/JDK-8046108).
To do that, just add this to your
[compileKotlin, compileTestKotlin]*.kotlinOptions*.javaParameters = true and you’ll be using the parameters.
Else, the first test will fail on the first assertion and not on the second one.
If you need help to set up gradle to test this, you can have a look at my
I learned this the hard way but in the end I opted to not mock any object I was requiring, but using constant value objects.
For example, if I am testing underage people I will have a
val UNDERAGE_PERSON = DataClass("underage, 1) on an
TestExtensions.kt file instead of using a mocked object, at least for POJOs, DTOs and other stateful constructs.
For stateless and business logic methods, I am hesitant until I inspect mockk code to see what is it doing to mess with
Some days later, I experienced the same bug with mockito,
but only when
@Mock annotations where used instead of using
I was unable to reproduce it on a constant behavior as apparently this happened randomly.
Update: The mockk issue fas fixed on
mockk 1.8.7 release, less than 10 days after I submitted the bug.
That is grade A job from the maintainers. Kudos!
Originally published in Medium