Spring Security JUnit Testing

Spring Security JUnit Testing

ยท

3 min read

Unit testing is one of the tests performed by developers in which individual units of source code are tested to determine whether they are fit for use.

In this tutorial, we will learn to write JUnit test cases for applications secured with Spring Security and JWT.

JUnit for Database-based Authentication

  • Project Setup

The application we are going to use here is a basic Spring Boot application with a couple of RestAPIs which are protected with Spring Security.

The detailed implementation of the application can be found in the previous article @ Spring Security Database-based Authentication

  • Implementation

We need to configure some test users with different roles which will be passed in the JUnit Test case using @WithUserDetails.

SpringSecurityTestConfig.java

@TestConfiguration
public class SpringSecurityTestConfig {

    @Bean
    @Primary
    public UserDetailsService userDetailsService() {
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

        final User.UserBuilder userBuilder = User.builder().passwordEncoder(passwordEncoder::encode);
        List<GrantedAuthority> adminAuthorities = AuthorityUtils.createAuthorityList("ROLE_ADMIN");
        List<GrantedAuthority> userAuthorities = AuthorityUtils.createAuthorityList("ROLE_USER");

        UserDetails adminUser = userBuilder.username("shail@mail.com").password("shail@123").authorities(adminAuthorities).build();
        UserDetails userUser = userBuilder.username("john@mail.com").password("john@123").authorities(userAuthorities).build();

        return new InMemoryUserDetailsManager(Arrays.asList(adminUser, userUser));
    }

}

SecurityApplicationTests.java

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringSecurityTestConfig.class)    //Passing the security config class details.
@AutoConfigureMockMvc
class SecurityApplicationTests {

    @Autowired
    private MockMvc mockMvc;

    @Autowired @InjectMocks
    private MessageController messageController;

    @MockBean
    private SecurityContext securityContext;

    @Test
    void contextLoads() {

        Assertions.assertNotNull(messageController);
    }

    // Testing if the user with ROLE_ADMIN is able to access the admin api.
    @Test
    @WithUserDetails("shail@mail.com")
    public void adminMessageTestSuccess() throws Exception{
        mockMvc.perform(MockMvcRequestBuilders.get("/api/admin/message").contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk());
    }

    //Testing fail case where user having ROLE_USER should be denied access.
    @Test
    @WithUserDetails("john@mail.com")
    public void adminMessageTestFail() throws Exception{
        mockMvc.perform(MockMvcRequestBuilders.get("/api/admin/message").contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isForbidden());
    }

}

JUnit for JWT-based Authentication

  • Project Setup

The application we are going to use here is a basic Spring Boot application with a couple of RestAPIs which are protected with Spring Security.

The detailed implementation of the application can be found in the previous article @ Spring Security with JWT

To test our RestAPIs which are secured using JWT-based security we will mock our UserDetailsService loadUserByUsername method to always return a mock user.

Mocking the service implementation will help us prevent any real interaction with the database and at the same time, we can test our APIs with test user details. Then we will generate a JWT token for our test user and pass this token in our JUnit test case.

SecurityApplicationTests.java

@SpringBootTest
@AutoConfigureMockMvc
class SecurityApplicationTests {

    @Autowired
    private MockMvc mockMvc;

    @Autowired @InjectMocks
    private MessageController messageController;

    @Autowired
    private TokenHelper tokenHelper;

    @Autowired
    private JwtTokenAuthenticationFilter filter;

    @MockBean
    private UserDetailsServiceImpl userDetailsService;

    private  String token;

    @BeforeEach
    public void setup() {
        List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ADMIN");
        Mockito.when(userDetailsService.loadUserByUsername(Mockito.anyString())).thenReturn(new User("test-user", "test-password", authorities));
        token = tokenHelper.generateToken("test-user", authorities);
    }


    @Test
    public void adminMessageTestSuccess() throws Exception {

        mockMvc.perform(MockMvcRequestBuilders.get("/api/admin/message").header("AUTHORIZATION","Bearer "+token).contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk());
    }

    @Test
    public void adminMessageTestFail() throws Exception {
        String wrongToken = "some_wrong_token";

        mockMvc.perform(MockMvcRequestBuilders.get("/api/admin/message").header("AUTHORIZATION","Bearer "+wrongToken).contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isForbidden());

    }
}

NOTE: The implementation to test JWT authentication will vary based on the implementation of your security implementation.

Conclusion

In this article, we learned to write JUnit test cases for Spring Boot applications when they are secured with Spring Security and JWT.

The complete source code of this article can be found over Github.

i. JUnit for Database-based Authentication

ii. JUnit for JWT-based Authentication

ย