Home > OS >  Error when trying to test controller endpoints of a project configured with Spring Security JWT
Error when trying to test controller endpoints of a project configured with Spring Security JWT

Time:09-15

I'm trying to use JUnit to test the controller endpoints of a project that is configured with Spring Security JWT, but it's throwing an exception when creating a bean with the name 'securityConfig' because it needs a bean of type 'jwtUtil' that doesn't can be found.

When I start the application and run some tests with Postman everything works perfectly, but with JUnit it's giving this problem.

Does anyone know how I can resolve it?

SecurityConfig

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Autowired
    private Environment env;
    
    @Autowired
    private JWTUtil jwtUtil;
    
    public static final String[] PUBLIC_MATCHERS = {
        "/h2-console/**"
    };
    
    public static final String[] PUBLIC_MATCHERS_GET = {
        "/comics/**",
        "/reviews/**",
        "/comments/**"
    };
    
    public static final String[] PUBLIC_MATCHERS_POST = {
            "/users/**"
    };
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        
        if (Arrays.asList(env.getActiveProfiles()).contains("test")) {
            http.headers().frameOptions().disable();
        }
        
        http.cors().and().csrf().disable();
        http.authorizeRequests()
            .antMatchers(HttpMethod.POST, PUBLIC_MATCHERS_POST).permitAll()
            .antMatchers(HttpMethod.GET, PUBLIC_MATCHERS_GET).permitAll()
            .antMatchers(PUBLIC_MATCHERS).permitAll()
            .anyRequest().authenticated();
        http.addFilter(new JWTAuthenticationFilter(authenticationManager(), jwtUtil));
        http.addFilter(new JWTAuthorizationFilter(authenticationManager(), jwtUtil, userDetailsService));

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
    
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
    
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues();
        
        configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "DELETE", "OPTIONS"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

JWTUtil

@Component
public class JWTUtil {
    
    @Value("${jwt.secret}")
    private String secret;
    
    @Value("${jwt.expiration}")
    private Long expiration;
    
    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis()   expiration))
                .signWith(SignatureAlgorithm.HS512, secret.getBytes())
                .compact();
    }
    
    public boolean validToken(String token) {
        Claims claims = getClaims(token);
        if (claims != null) {
            String username = claims.getSubject();
            Date expirationDate = claims.getExpiration();
            Date now = new Date(System.currentTimeMillis());

            if (username != null && expirationDate != null && now.before(expirationDate)) {
                return true;
            }
        }
        return false;
    }
    
    public String getUsername(String token) {
        Claims claims = getClaims(token);
        if (claims != null) {
            return claims.getSubject();
        }
        return null;
    }
    
    private Claims getClaims(String token) {
        try {
            return Jwts.parser().setSigningKey(secret.getBytes()).parseClaimsJws(token).getBody();
        } catch(Exception e) {
            return null;
        }       
    }

}

UserControllerTest

@ExtendWith(SpringExtension.class)
@ActiveProfiles("test")
@WebMvcTest(controllers = UserController.class)
@AutoConfigureMockMvc
public class UserControllerTest {
    
    static String USER_API = "/users";
    
    @Autowired
    MockMvc mvc;
    
    @MockBean
    UserService userService;
    
    ObjectMapper mapper;
    
    @BeforeEach
    public void setUp() {
        mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
    
    @Test
    @DisplayName("Must save a user")
    public void saveUserTest() throws Exception {
        // Scenario     
        long id = 2l;
        
        UserNewDTO newUser = UserNewDtoBuilder.aUserNewDTO().now();
        UserDTO savedUser = UserDtoBuilder.aUserDTO().withId(id).now();
        
        BDDMockito.given(userService.save(Mockito.any(UserNewDTO.class))).willReturn(savedUser);
        
        String json = mapper.writeValueAsString(newUser);
        
        // Execution
        MockHttpServletRequestBuilder request = MockMvcRequestBuilders
                                                    .post(USER_API)
                                                    .contentType(MediaType.APPLICATION_JSON)
                                                    .accept(MediaType.APPLICATION_JSON)
                                                    .content(json);     
        // Verification
        mvc
        .perform(request)
        .andExpect( MockMvcResultMatchers.status().isCreated() )
        .andExpect( MockMvcResultMatchers.header().string(HttpHeaders.LOCATION, Matchers.containsString("/users/" id)));
    }
}

Exception

2022-09-14 10:13:33.423  WARN 2092 --- [           main] o.s.w.c.s.GenericWebApplicationContext   : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfig': Unsatisfied dependency expressed through field 'jwtUtil'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.gustavo.comicreviewapi.security.JWTUtil' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
2022-09-14 10:13:33.432  INFO 2092 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-09-14 10:13:33.745 ERROR 2092 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field jwtUtil in com.gustavo.comicreviewapi.configs.SecurityConfig required a bean of type 'com.gustavo.comicreviewapi.security.JWTUtil' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.gustavo.comicreviewapi.security.JWTUtil' in your configuration.

2022-09-14 10:13:33.766 ERROR 2092 --- [           main] o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@64c63c79] to prepare test instance [com.gustavo.comicreviewapi.resources.UserControllerTest@7302ff13]

java.lang.IllegalStateException: Failed to load ApplicationContext

CodePudding user response:

Since you are using @WebMvcTest, Spring Boot will only consider the Spring MVC components as beans.

From the @WebMvcTest javadoc, emphasis by me:

Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. @Controller, @ControllerAdvice, @JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not @Component, @Service or @Repository beans).

Since your JWTUtil is a @Component, you have to mock that bean as well:

@MockBean
JWTUtil jwtUtil;
  • Related