Kotlin Unit Tests with Parameters
One thing we can all agree on is that any project can benefit from well written unit tests. There are some cases where writing unit tests feels repetitive and sometimes we can end up duplicating code just to change the input and the expected output.
In order to give an example of a situation like that, let’s create a class whose responsibility is to validate that a password matches a set of criteria. Below is an example of such a class, that has a method which receives the password to be checked and return true, if the password meets the requirements. The password should have at least one upper case letter, one digit, one special character and minimum 8 characters.
Writing unit tests for this class can quickly turn into a lot of duplicate code, as all of them are following the same template: “Given a password, When validating it, Then it is reported as valid/invalid”. We can definitely do better than that and we can count on JUnit 5 for it.
Parameterized tests
Parameterized tests have been added since JUnit 4, but in this article we are going to use JUnit 5. They allow us to create a test and run it using different parameters. The only thing we need to do in order to run a Parameterized test is to annotate the test with @ParameterizedTest and provide a data source for it. JUnit 5 provides a generous amount of ways to pass the arguments data, as they can be retrieved from methods, enums, predefined collections and even CSV files. You can find more examples and details in the official documentation. Below, you can find an example of how a parameterized test for our method looks like.
If you are using this in an Android project, make sure you configure Kotlin to use Java 8 compilation target, so you can use Stream and Arguments. Using Arguments helps us passing the parameters without creating another class to encapsulate them.
First, we are annotating the test with @ParameterizedTest and provide the name we want to be used for our test. We can use {n} to access the nth parameter. In this example, we are fetching the arguments from a method, which needs to be static. Since we are using Kotlin, we have to put it into a companion object.
One of the main advantages of this approach, besides the reduced boilerplate code, is that for every argument the function behaves as a normal test, so the Before and After methods are called for each one of them. This can be a great thing if we need to do some initialization and clean up, but for us this is not the case. I couldn’t find a way to customize the test name besides adding the arguments in it, so for the expected result output I was stuck with the true/false words.
Dynamic Tests
JUnit 5 brings us a new approach to writing unit tests. While common tests are static, as they are fully specified at the compile time, dynamic tests are generated at runtime by using the @TestFactory annotation. By doing this, the method is marked as a factory for test cases.
One key aspect that we need to keep in mind when using dynamic tests is that their execution lifecycle is different than normal tests, since all the before and after methods are called only at the beginning and at the end of the factory method, instead of each generated test. This property excludes dynamic tests from your options if you need to do some initializations or clean up for your test. However, our password validator needs no setup, so we can safely use dynamic tests in this case. Below, there is an example of how we can do this.
I find this approach easier to write and understand. We are annotating our class with the TestFactory annotation, build the list and then use the parameters to test our validator. What I like about it is that the parameters and the rest of the test are placed together. So, if I have multiple tests that require parameters, I don’t have to scroll to the bottom of the screen to find a method in a companion object. Moreover, you can customize the name of the test even more, thus we can get rid of the true/false words from the name and replace them with valid and invalid, which are more suitable for our tests.
To sum up, these are two ways you can easily reduce the boilerplate code by writing unit tests that accept parameters in Kotlin using JUnit 5. Both of them have advantages and disadvantages, it’s up to you to decide which one fits better for your tests. For further information regarding them, don’t hesitate to check out the official documentation, as it has plenty of examples to help you understand how to use them.
I would really appreciate if you give me your opinion on these approaches. I hope you find this article helpful.
Thank you!