JUnit 5 parallel test execution and singleton/static state in framework library - junit5

Actually I want to speed up my JUnit 5 tests with parallel test execution. But can't find any convenient way to avoid problems with static state in third-party frameworks: each thread use same class loader and as result influence through common state to another test in neighbor thread.
There is small snippet of my test configuration with #Execution annotation:
#Execution(ExecutionMode.CONCURRENT)
class Test {
fun logClassLoader() {
println(SomeClass::class.java.classLoader);
}
#Test
#Execution(ExecutionMode.CONCURRENT)
fun test() {
Thread.sleep(1000)
logClassLoader()
}
#Test
#Execution(ExecutionMode.CONCURRENT)
fun test1() {
Thread.sleep(1000)
logClassLoader()
}
}
class SomeClass
Seems most simplest solution to configure execution that each thread uses it's own ClassLoader with own loaded framework.
Is there any convinient for JUnit 5 way to do so?

Related

Mocking Gradle javaexec calls with mockK

Currently a beginner with mockK as it relates to developing Gradle plugin code in Kotlin. Suppose I have this class:
abstract class MyDomainObjectClass #Inject constructor(private val execOps: ExecOperations) {
abstract val mainClass: Property<String>
fun run() {
execOps.javaexec {
// ...
}
}
}
What I want to do is to construct a MyDomainObjectClass (normally constructed using something like ObjectFactory.newInstance()) in such a way that I can pass in a mocked ExecOperations, so that I can verify that the javaexec call is called exactly once (with the verification possibly involving mainClass, if I can find a way to involve it).
Is there a way I can satisfy all these requirements, or am I better off with a constructed mock of MyDomainObjectClass (stubbing in mainClass in the process)?

import a test interface from a library's tests into application's tests

I made a library. OrderedCollections. I import and use in an application. In the library is a junit5 test interface. I want to use the test interface in the application.
example, in library you have
interface BinaryTree<K:Comparable<K>,V> : Collection<Pair<K,V> {...}
in The tests for this library you have
interface TreeTests<K : Comparable<K>, V> {
val tree: BinaryTree<K, V>
#Test
fun sorted() {
val list = tree.toList().sortedBy { it.first }
assertIterableEquals(list, tree)
}
}
I made an implementation in the application of BinaryTree, but it is backed by sqlite. I want to use the test interface from the library on the implementation in the application. Like this
class TreeDB:BinaryTree<Int,String>(){...}
In the tests in the application
class TreeDBTests:TreeTests<Int,String> {
override val tree = TreeDB()
}
And then all the tests written for a possible implemention of BinaryTree are run on the TreeDB class.
However, code written in the tests of a library are not exported to consumers of the library. Anyway to make this work?
I recommend to treat test utils as just any other library that serves a purpose. If, as it seems to be in your case, those test utils are also required for testing the original lib, you will have to separate interface and implementation from the original lib, e.g.
bintree-interface <– bintree-testutils (TreeTests etc.)
^– bintree-implementation
Now the tests for bintree-implementation can use TreeTests and your tests for TreeDB can as well.

How to apply a Rule to all test cases in a AndroidJUnitRunner?

I'm trying to apply a TestWatcher as a rule across all my test cases run by a particular runner.
MetadataCollector:
class MetadataCollector : TestWatcher() { ... }
TestRunner:
class TestRunner : AndroidJUnitRunner() {
override fun onCreate(arguments: Bundle?) {
super.onCreate(arguments)
}
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, TestApplication::class.java.name, context)
}
}
All of my test classes currently require MetadataCollector() to be initialized as a Rule:
Test Class:
#JvmField #Rule val collector = MetadataCollector()
Is there a way I can create an instance of this rule automatically to each test case from the runner? Ideally, this is to avoid duplicating this #Rule creation in every single Test Class.
I am unfortunately stuck with JUnit 4 at the moment. :(
There is a better way to do this, by injecting an instance of RunListener into your test runner. In your gradle config, you need to:
defaultConfig.testInstrumentationRunner = "com.mypackage.MyTestRunnerClassName"
defaultConfig.testInstrumentationRunnerArgument("listener", "com.mypackage.MyRunListenerClassName")
And in code, create a RunListenerClassName to implement the corresponding hooks
class MyRunListener : RunListener() {
// impl
}

Kotlintest extensions providing information back to the test

JUnit 5 has a neat extensions functionality which is not compatible with kotlintest even if it runs on JUnit framework. While the simple use cases in which we just need to log something can be handled by the TestListener, we cannot handle more advanced cases. In particular, how to interact with the extension? Ideally, I would like to get a hold of the extension so I could query it.
In JUnit5 it would be (one of the options anyway)
#ExtendWith(MyExtension.class)
class Something() {
#MyAnnotation
MyType myType;
#Test
void doSomething() {
myType.doSomething();
}
}
In JUnit4 it would be even simpler
#Rule
MyRule myRule;
#Test
void fun() {
myRule.something();
}
Of course, there is a SpringExtension but it does the reflective instantiation of the class.
Is there any way to do it easier?
You can keep a reference to an extension/listener before passing it to the overriden function.
For example:
val myListener = MyKotlinTestListener()
val myOtherListener = MyOtherKotlinTestListener()
override fun listeners() = listOf(myListener, myOtherListener)
This way you can do what you want in your tests with that reference available
test("MyTest") {
myListener.executeX()
}
The same goes if you're using an extension of any sort.
They'll still be executed as part of KotlinTest's lifecycle!

How to create a TestContainers base test class in Kotlin with JUnit 5

I am trying to use Neo4j TestContainers with Kotlin, Spring Data Neo4j, Spring Boot and JUnit 5. I have a lot of tests that require to use the test container. Ideally, I would like to avoid copying the container definition and configuration in each test class.
Currently I have something like:
#Testcontainers
#DataNeo4jTest
#Import(Neo4jConfiguration::class, Neo4jTestConfiguration::class)
class ContainerTest(#Autowired private val repository: XYZRepository) {
companion object {
const val IMAGE_NAME = "neo4j"
const val TAG_NAME = "3.5.5"
#Container
#JvmStatic
val databaseServer: KtNeo4jContainer = KtNeo4jContainer("$IMAGE_NAME:$TAG_NAME")
.withoutAuthentication()
}
#TestConfiguration
internal class Config {
#Bean
fun configuration(): Configuration = Configuration.Builder()
.uri(databaseServer.getBoltUrl())
.build()
}
#Test
#DisplayName("Create xyz")
fun testCreateXYZ() {
// ...
}
}
class KtNeo4jContainer(val imageName: String) : Neo4jContainer<KtNeo4jContainer>(imageName)
How can I extract the databaseServer definition and the #TestConfiguration? I tried different ways of creating a base class and having the ContainerTest extend it, but it is not working. From what I understand, static attriubutes are not inherited in Kotlin.
Below my solution for sharing same container between tests.
#Testcontainers
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
abstract class IntegrationTest {
companion object {
#JvmStatic
private val mongoDBContainer = MongoDBContainer(DockerImageName.parse("mongo:4.0.10"))
.waitingFor(HostPortWaitStrategy())
#BeforeAll
#JvmStatic
fun beforeAll() {
mongoDBContainer.start()
}
#JvmStatic
#DynamicPropertySource
fun registerDynamicProperties(registry: DynamicPropertyRegistry) {
registry.add("spring.data.mongodb.host", mongoDBContainer::getHost)
registry.add("spring.data.mongodb.port", mongoDBContainer::getFirstMappedPort)
}
}
}
The key here is to not use #Container annotation as it will close just created container after your first test subclass executes all tests.
Method start() in beforeAll() initialize container only once (upon first subclass test execution), then does nothing while container is running.
By theory we shouldn't have to do this hack, based on:
https://www.testcontainers.org/test_framework_integration/junit_5/
...container that is static should not be closed until all of tests of all subclasses are finished, but it's not working that way and I don't know why. Would be nice to have some answer on that :).
I've had the same issue (making Spring Boot + Kotlin + Testcontainers work together) and after searching the web for (quite) a while I found this nice solution: https://github.com/larmic/testcontainers-junit5. You'll just have to adopt it to your database.
I faced very similar issue in Kotlin and spring boot 2.4.0.
The way you can reuse one testcontainer configuration can be achieved through initializers, e.g.:
https://dev.to/silaev/the-testcontainers-mongodb-module-and-spring-data-mongodb-in-action-53ng or https://nirajsonawane.github.io/2019/12/25/Testcontainers-With-Spring-Boot-For-Integration-Testing/ (java versions)
I wanted to use also new approach of having dynamicProperties and it worked out of a boxed in java. In Kotlin I made sth like this (I wasn't able to make #Testcontainer annotations working for some reason). It's not very elegant but pretty simple solution that worked for me:
MongoContainerConfig class:
import org.testcontainers.containers.MongoDBContainer
class MongoContainerConfig {
companion object {
#JvmStatic
val mongoDBContainer = MongoDBContainer("mongo:4.4.2")
}
init {
mongoDBContainer.start()
}
}
Test class:
#SpringBootTest(
classes = [MongoContainerConfig::class]
)
internal class SomeTest {
companion object {
#JvmStatic
#DynamicPropertySource
fun setProperties(registry: DynamicPropertyRegistry) {
registry.add("mongodb.uri") {
MongoContainerConfig.mongoDBContainer.replicaSetUrl
}
}
}
Disadvantage is this block with properties in every test class what suggests that maybe approach with initializers is desired here.