I have a Kotlin project, using JDK 17, Junit 5.10.0, and junit-platform 1.10.0 (the latest Junit versions as of today) with several thousands of unit tests.
Because of some framework choices we made, we are forced to run tests in sequential mode: namely, we use Exposed
, which is not easy to mock. Some of our services in which we inject DAOs use Exposed
transactions, which we are forced to mock - injecting mock DAOs is not enough. Exposed
uses some static calls, so we had to introduce Mockk
just for the purpose of mocking these. The tests that need transactions to be mocked have a special tag, that a custom BeforeEachCallback
/ AfterEachCallbackextension
sees and configures the mock accordingly.
private fun mockExposedTransaction() {
val database = mockk<Database>()
val datasource = mockk<DataSource>()
val transaction = mockk<Transaction>()
val transactionManager = mockk<TransactionManager>()
mockkObject(Database)
every { Database.connect(datasource) } answers { database }
mockkObject(TransactionManager)
every { TransactionManager.resetCurrent(any()) } answers {}
every { TransactionManager.currentOrNull() } answers { transaction }
every { TransactionManager.managerFor(any()) } answers { transactionManager }
every { transactionManager.currentOrNull() } answers { transaction }
every { transactionManager.defaultIsolationLevel } answers { 0 }
every { transactionManager.defaultRepetitionAttempts } answers { 0 }
every { transactionManager.bindTransactionToThread(eq(transaction)) } answers { }
every { transactionManager.newTransaction(eq(0), eq(transaction)) } answers { transaction }
every { transaction.db } answers { database }
every { database.useNestedTransactions } answers { false }
}
This works OK, but only in sequential mode. When running those tests with @Execution(ExecutionMode.CONCURRENT)
, then Mockk gets confused mixing the mocks between different threads, as mockkObject
is not threadsafe. That's the way it is, and we accept it.
our junit-platform.properties
currently has junit.jupiter.execution.parallel.enabled
as false
, but we would like to change that and enable concurrent mode on some individual tests, as we have tests that don't target objects that use Exposed
. That's what Junit documentation recommends :
Please note that enabling this property is only the first step required to execute tests in parallel. If enabled, test classes and methods will still be executed sequentially by default. Whether or not a node in the test tree is executed concurrently is controlled by its execution mode.
so I am switching junit.jupiter.execution.parallel.enabled
to true
: running the tests, it's passing - normal.
Then, I am adding @Execution(ExecutionMode.CONCURRENT)
on a simple test that does NOT need transaction mocking.
The test methods on this test run in parallel and are all passing (as expected), but surprisingly, another unrelated test that needs transaction mocking, fails, with an error suggesting that the Exposed
mocks haven't been properly set up (yet ?) :
Please call Database.connect() before using this code
When I run this test individually, it passes.
So I am confused because that's not what I expect from reading the documentation: my understanding is that only the test annotated with @Execution(ExecutionMode.CONCURRENT)
should run in parallel, the rest of the tests should not be impacted at all. But it doesn't seem it's the case, as I observe unexpected side effects as soon as there's one test with ExecutionMode.CONCURRENT
.
Any idea of what could happen? should I open a bug on the Junit5 side?