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.
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.
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.
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:
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.
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 {
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:
Photo by Georgia Maciel from Pexels
Spring Security allows us to use role-based control to restrict access to API resources. However,…
Delegating user management to Keycloak allows us to better focus on meeting the business needs…
Swagger offers various methods to authorize requests to our Keycloak secured API. I'll show you…
Configuring our Spring Boot API to use Keycloak as an authentication and authorization server can…
Keycloak provides simple integration with Spring applications. As a result, we can easily configure our…
Postman comes with a wide variety of OAuth 2.0 compliant configuration options that allow us…