user-icon Tioman Gally
11. November 2021
timer-icon 4 min

JUnit5 Custom Extension: ParameterResolver with Kotlin

In my project I really need to generate objects with different ids which I can use in my tests. I know a lot of developers who write tests. And I also think most of them are using JUnit 5. JUnit 5 or Jupiter how the cool guys on the block call the testing framework is perfect for writing unit tests and also custom extensions. In my case I need to write a custom extension!
Jupiter is the alias of JUnit 5

Lets start with the question you have in your mind. What is a JUnit5 custom Extension?

A JUnit5 custom extension extends your current Unit Test with your reusable custom code. Extensions are like interceptors. JUnit provides interfaces which you can implement to execute your code. For example BeforeAllCallback interface runs before @BeforeAll, BeforeEachCallback runs before @BeforeEach. If you want to know more just click me. But it also provides interfaces like the one we will implement now to provide custom parameters which you can inject in your test. The ParameterResolver interface.

But how can I write a JUnit5 custom extension?

Let us take a look on the IdGenerator Annotation. The annotation itself contains a class which is used to extend the IdGenerator annotation via @ExtendWith . The Class IdGeneratorExtension implements the ParameterResolver interface which contains two functions.

supportsParameter : This defines if a parameter will be injected into the test function or not. This function needs to return therefore a boolean. And only if this function returns true the other function resolveParameter will be called.

ℹ️ In my case I only return true if the parameter of the test function which I will show you in the end is one of my supported objects.

resolveParameter : This function returns then the object which will be passed to the test function as a parameter.

ℹ️ In my case I calculate an Id with generateId(). In the next step I check the type of the parameter of the testing function using the parameterContext . So if my unit test adds a parameter of type UserId I will pass an UserId object.

How do I add a JUnit5 custom extension in my test?

Easier than you think 🐶

Thats it. Nothing fancy 😉 Just use the annotation on class level and add as many parameters you want 🙆

The magic is done on the annotation itself because the @ExtendWith was used while declaring the @IdGenerator annotation.

The interesting part is that for each parameter of my test-function the function resolveParameter of the ParameterResolver is called. Therefore I can add as much parameters as I want and every parameter will have its value (when I handled the type of the parameter in my custom extension function of course 😉 )

In my test it is really important that each id is unique. And to guarantee this we somehow need to check that the generated id was not generated before. Although the chance that this can happen is really low 😅

I need to save a state so how can I use the store in Jupiter Extensions?

What has changed?

  1. I create now a STORE in the the private companion object which creates a Jupiter store for the test instance. The store is like a key value map which is persistent while your test instance is present.
  2. I implemented a new interface called BeforeAllCallback which forces you to implement the beforeAll function. This is executed before all Tests of your test class (also before each @Nested inner classes). Inside that function we take advantage of the store.
    We get the store via the STORE variable which was created in 1). After we got the store we set a new entry with the key IDS . As a value we create an empty mutableSetOf<String>() .
  3. What else? Now we need to adjust the resolveParameter function. There we now get our created mutableSetOf<String>() by accessing the key IDS .
  4. And here we just check if the generated id from the line before is already present. If yes, call the whole function again to generate a new id. If no, add the id to the mutableSet and put the set into the store.

Cool! Now we prevent duplicate same ids. Easy right? 😎

Image Sources: Planet Volumes @

Comment article