Please read this before using @BeforeClass in Kotlin JUnit tests!

It’s all about the nuances in the Kotlin-Java interop story

The Android Developer
5 min readFeb 26, 2023

Introduction

Hello everyone, hope you’re doing well. In this article, we’ll have a look at a ‘gotcha’ that is related to writing JUnit tests with Kotlin. It mainly revolves around using the @BeforeClass annotation and how the Kotlin-Java interop works. I personally think that this is important to be known by anyone who uses JUnit for unit tests in Kotlin. More importantly, once you know about this, you won’t waste time debugging this “issue” when it happens, since you already know how to “fix” it.

The @BeforeClass annotation

The @BeforeClass annotation is used to run a function that will be executed once, before all the tests are run. This is different from the @Before annotation, which runs a function before running every test. The @BeforeClass annotation becomes useful in scenarios where some one-time initialization(s) must be done before all tests are executed.

For example, let’s say you want to test the implementation of a remote database. It makes sense to run all the tests on a locally hosted database, without running the test on the actual, production database. We can use the @BeforeClass annotation to ensure that all the tests run on the locally hosted database.

class Tests {
companion object {
// THERE IS AN ISSUE WHEN USING THE @BeforeClass ANNOTATION LIKE THIS.
// PLEASE DO NOT DO THIS. THIS IS JUST FOR DEMONSTRATION PURPOSES.
@BeforeClass
fun setup() {
// setup database to run the tests locally
}
}

@Test
fun example_test() {
// your test
}
}

The function annotated with @BeforeClassmust be a static method. Since there is no concept of static methods in Kotlin, we might intuitively put the method in a companion object. Spoiler alert, this is where the problem lies.

The problem

Let’s check if the method marked with @BeforeClass gets executed before the first test.

class Tests {
companion object {
// THERE IS AN ISSUE WHEN USING THE @BeforeClass ANNOTATION LIKE THIS.
// PLEASE DO NOT DO THIS. THIS IS JUST FOR DEMONSTRATION PURPOSES.
@BeforeClass
fun setup() {
println("setup_executed")
}
}
@Test
fun example_test() {
println("example_test_executed")
}
}

Let’s run the test class and check whether the methods are executing in the expected order. Remember, we are expecting the method marked with @BeforeClassto get executed before example_test() method gets executed.

Huh! Not what you expected isn’t it? The setup() function doesn’t get executed. Only the example_test() method gets executed. Weird isn’t it? Why would this be? Well, it’s mainly because of how the Kotlin-Java interop works for companion objects in Kotlin.

Companion objects in Kotlin-Java interop

The problem mainly arises from the fact that JUnit is a unit testing framework for Java. It was never meant to be used with Kotlin. In fact, the reason it works with Kotlin is because Kotlin supports interoperability with Java. This means we can call Kotlin code from Java code, and vice-versa. But, there are some caveats associated with the Kotlin-Java interop. In this example, we need to know how companion objects are represented in Java, and how methods in those companion objects could be called in Java.

Let’s say we have a companion object in Kotlin, in the following manner.

class SomeClass {
companion object {
fun someMethod() {
// method body
}
}
}

Now, if we wanted to call someMethod() in Java, we’d need to do it in the following manner.

public class JavaClass {
public static void main(String[] args) {
SomeClass.Companion.someMethod();
}
}

We need to refer to the generated ‘Companion’ property of the Kotlin class and then call the static method.

The fix

This makes it abundantly clear as to why the method annotated with @BeforeClassdidn’t get executed in the first example. It’s because, JUnit, being a Java based framework, wasn’t able to detect any static method marked with the @BeforeClass that belonged to the test class. In simple terms, it didn't realize that there was a static method associated with the test class, and that, it had to access it in the following manner -Tests.Companion.setup(). This makes sense because, in the Java world, static methods of a class are never accessed like this.

To fix it, we need to annotate the method with another annotation called @JvmStatic, which will generate a static method that could be called in Java. This allows the method defined in the companion object to be called similar to how a normal static method would be called in Java. Let’s add this annotation and see if the method marked with @BeforeClassannotation gets executed before the test method.

class Tests {
companion object {
@BeforeClass // add this annotation
@JvmStatic
fun setup() {
println("setup_executed")
}
}

@Test
fun example_test() {
println("example_test_executed")
}
}

And, Voila! It works! Now that the static method in the companion object could be accessed as a normal static method in Java, JUnit detects it and executes it before the test method gets executed.

Conclusion

That wraps up this short blog post🎉! Hopefully, you found this blog post helpful! If you liked this article, feel free to check out my other articles as well. As always, I would like to thank you for taking the time to read this article😊. I wish you the best of luck! Happy coding 👨‍💻! Cheers!

If you really liked my article and want to support me, you can do so, by clicking this link. Thank you so much for being generous ❤️, it really motivates me to keep going, and it helps me to keep my articles free for anyone to read. If you don’t feel like supporting, that’s fine too! The fact that you took some time off your schedule to read my article means a lot to me. Thank you 🙂

--

--

The Android Developer

| A very passionate Android Developer 💚 | An extreme Kotlin fanatic 💜 | A huge fan of Jetpack Compose 💙| Focused on making quality blog posts 📝 |