Minimal Spring Security Context Setup for Unit Testing

Home » Blog » Software » Enterprise Software » Minimal Spring Security Context Setup for Unit Testing

Creating a minimal Spring Security setup for JUnit unit tests can be a tedious as well as somewhat daunting task. With its Authentication, Principal, GrantedAuthority, and UserDetails classes Spring Security is extremely flexible and configurable and probably accommodates pretty much every authentication and authorization use case under the sun.

However, in software architecture such flexibility often comes at price, the trade-off in this case being that simple use cases can require more implementation effort than one would expect. One such use case is mocking an authenticated user for unit tests.

While Spring Security already provides a convenient @WithMockUser annotation that does just that, that annotation only becomes active if a valid SecurityContext is available in our test environment.

Now, we might be tempted to use the @WebMvcTest Spring Boot slice, which indeed will auto-configure our security rules and allow us to easily mock the authenticated user.

However, that’s not how @WebMvcTest was intended to be used. The purpose of this annotation is to provide a valid environment for Spring Web MVC integration tests, e.g., tests that deal with the @Controller, @ControllerAdvice, or WebMvcConfigurer parts of an application.

If we just want to write a unit test that has nothing to do with HTTP endpoints or REST APIs, yet still requires authentication, using @WebMvcTest is a less than optimal solution. Not only will tests annotated as such run longer due to the additional Spring Boot context setup involved, but due to Spring’s dependency injection we also might run into unexpected behaviour when initialising or mocking the dependencies of our system under test. Usually, with JUnit tests, nowadays we’d use Mockito‘s @Mock or @MockBean annotations for mocking dependencies. However, we should avoid using @Mock and @MockBean in tandem in a single test class or using @MockBean for unit tests (as opposed to integration tests that require an actual Spring Boot environment).

Hence, a better approach is to do without @WebMvcTest in such cases and provide a minimal SecurityContext setup ourselves. Long story short, this is how to achieve just that:

Using the SecurityContextHolder provided by Spring Security we create an empty SecurityContext and afterwards use a TestingAuthenticationToken for setting the username as previously defined in the @WithMockUser annotation for the test method in question. This allows us to easily test for the username of an authenticated user without having to worry about the actual means of authentication or the – in many cases probably rather complex – context setup involved.

We might further refactor that setup to simplify our test method by providing a reusable factory responsible for that setup and simply calling a factory method where applicable:

Here are two GitHub Gists for the code shown above:

Thanks a lot to Philip Riecks, who pointed me in the right direction. Philip offers a highly recommended Testing Spring Boot Applications Masterclass you can check out here.

About the author: Bjoern
Independent IT consultant, entrepreneur

    Mentions

  • 💬 Clearing the Spring Security Context During Unit Testing | Björn Wilmsmann

Leave a Comment