Spring Boot

Create a custom annotation to configure Spring Boot tests

A custom annotation in Spring Boot tests is an easy and flexible way to provide the required configuration. We can use it to efficiently group all the annotations and configuration classes that we would otherwise apply to each test class separately.

Prerequisites

As an example for this article, I’m going to create a custom annotation to configure controller tests. If you want to recreate the work described below, you will need a Spring Boot project with a REST controller and some MVC tests.

Why use a custom annotation in Spring Boot tests

One reason is to simply group all the annotations we need to apply across multiple test classes into convenient metadata. Moreover, it’s also useful when you use @Import to register additional components required for configuring the “slice” of the system to test, as it reduces a lot of copy-and-paste code.

Another possible solution would be to create a base class and making each test extend it. However, inheritance comes with a lot of issues that I don’t want to deal with just to provide a common configuration for some tests.

A custom annotation that encloses all the common configurations for tests is both an elegant and simple solution. In the following example, instead of annotating my tests with @WebMvcTest and @Import, I’m going to create @RestControllerIntegrationTest which will take care of all the configuration details. Thanks to this, I will avoid duplicating the code or complicating it with unnecessary inheritance.

Create a custom annotation for MVC tests

First, I’m going to replace the @WebMvcTest annotation with a custom one. I’m going to create the following RestControllerIntegrationTest annotation:

package in.keepgrowing.keycloakspringboot.testing.annotations;

import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.ContextConfiguration;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@WebMvcTest
public @interface RestControllerIntegrationTest {

    /**
     * @see WebMvcTest#value
     */
    @AliasFor(annotation = WebMvcTest.class, attribute = "value")
    Class<?>[] value() default {};

    /**
     * @see WebMvcTest#controllers
     */
    @AliasFor(annotation = WebMvcTest.class, attribute = "controllers")
    Class<?>[] controllers() default {};
}

Let’s take a look at the annotations I’m using here:

  1. @Retention – specifies how long an annotation is to be retained. The RUNTIME Retention Policy enables the annotation to be retained by the VM at run time.
  2. @Target – puts constrains on the usage of the annotation. The TYPE Element Type will allow using it on my test classes.
  3. @WebMvcTest – since I am creating my own annotation for MVC tests, i need to include this Spring Boot annotation to still benefit from all the default features.

Thus, I can now replace @WebMvcTest with @RestControllerIntegrationTest and still have the same functionality:

package in.keepgrowing.keycloakspringboot.products.adapters.driving.api.http.controllers;

import in.keepgrowing.keycloakspringboot.testing.annotations.RestControllerIntegrationTest;
…
@RestControllerIntegrationTest(value = ProductController.class)
class ProductControllerTest {
…

Now, I can add any additional configuration my test “slice” requires to the custom annotation.

Provide additional configuration

In my example project, I’m using the @PreAuthorize annotation to restrict user access to the API endpoints. Therefore, I need to enable method-level security. Without it, the following test fails because it is missing the configuration required to apply the appropriate access control:

For that reason, I’m going to create a configuration class and annotate it with @EnableMethodSecurity:

package in.keepgrowing.keycloakspringboot.testing.config;

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@TestConfiguration
@EnableMethodSecurity
public class ControllerIntegrationTestConfig {
}

Next, I’ll load it into ApplicationContext using @ContextConfiguration in my custom annotation:

package in.keepgrowing.keycloakspringboot.testing.annotations;

import in.keepgrowing.keycloakspringboot.testing.config.ControllerIntegrationTestConfig;
…
@ContextConfiguration(classes = ControllerIntegrationTestConfig.class)
public @interface RestControllerIntegrationTest {

Verify results

As a result, all tests in classes annotated with @RestControllerIntegrationTest will be able to use method-level security. The following screenshot shows that my tests are passing now:

Read more about setting up tests in Spring Boot

Photo by Georgia Maciel from Pexels

little_pinecone

Recent Posts

Simplify the management of user roles in Spring Boot

Spring Security allows us to use role-based control to restrict access to API resources. However,…

3 years ago

Keycloak with Spring Boot #4 – Simple guide for roles and authorities

Delegating user management to Keycloak allows us to better focus on meeting the business needs…

3 years ago

Keycloak with Spring Boot #3 – How to authorize requests in Swagger UI

Swagger offers various methods to authorize requests to our Keycloak secured API. I'll show you…

3 years ago

Keycloak with Spring Boot #2 – Spring Security instead of Keycloak in tests

Configuring our Spring Boot API to use Keycloak as an authentication and authorization server can…

3 years ago

Keycloak with Spring Boot #1 – Configure Spring Security with Keycloak

Keycloak provides simple integration with Spring applications. As a result, we can easily configure our…

3 years ago

Kecloak in Docker #7 – How to authorize requests via Postman

Postman comes with a wide variety of OAuth 2.0 compliant configuration options that allow us…

3 years ago