简体   繁体   中英

Spring MVC integration test with h2 fails

I have been trying to write some integration tests using my in-memory database h2 and I don't know why, but it seems that the data from the database is inaccessible for the tests. When I run the application, the data is shown as per my import.sql file. This is the only row from my db in h2:

insert into agents values(1, 'Tom', 'Jay', 'tom@gmail.com', 1);

Now this is my method in my controller, to retrieve the agents based on an agency id:

@Controller
public class AgentController {

    @Autowired
    private AgentService agentService;

    @Autowired
    public AgentController(AgentService agentService) {
        this.agentService = agentService;
    }

    @Autowired
    private AgentMapper agentMapper;

    @GetMapping("/viewAgentsPage/{id}")
    public String viewAgentsPage(@PathVariable(value = "id") Long id, Model model) {
        model.addAttribute("listAgents", agentService.listByAgency(id));
        return "agents";
    }
}

And I've written two integration tests, as it follows:

@SpringBootTest(classes = AgentController.class)
@ContextConfiguration(classes = ProjectApplication.class)
@TestPropertySource(locations = "classpath:application-h2.properties")
@AutoConfigureMockMvc
@RunWith(SpringRunner.class)
@WithMockUser(username = "user1@gmail.com")
@ActiveProfiles("H2")
public class AgentControllerTest {
    @Autowired
    MockMvc mockMvc;

@Test//works fine
    public void showByIdMvc() throws Exception {
        mockMvc.perform(get("/viewAgentsPage/{id}", 1L))
                .andExpect(status().isOk())
                .andExpect(view().name("agents"));
    }

@Test//doesn't work
    public void showByIdNotFoundMvc() throws Exception {
        mockMvc.perform(get("/viewAgentsPage/{id}",18L))
                .andExpect(status().isNotFound())
                .andExpect(view().name("error"));
    }
}

On the second test I am expecting to get an exception, since I have no agent with the agency id 18 in my h2 database, still, the returned status from the test is 200 ok. Why is that? It seems like the data from the db is not accesses. This is the error from the second test:

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /viewAgentsPage/18
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {SPRING_SECURITY_CONTEXT=SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=user1@gmail.com, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]], Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_USER]]]}

Handler:
             Type = com.example.awbdproject.controllers.AgentController
           Method = com.example.awbdproject.controllers.AgentController#viewAgentsPage(Long, Model)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = agents
             View = null
        Attribute = listAgents
            value = []

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Language:"en", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = agents
   Redirected URL = null
          Cookies = []

java.lang.AssertionError: Status expected:<404> but was:<200>
Expected :404
Actual   :200

Also, here is my method in the agent service:

public List<AgentDto> listByAgency(Long id) {

        Optional<Agency> optionalAgency = agencyService.getAgencyById(id);

        if(!optionalAgency.isPresent()) {
            throw new AgencyNotFoundException("Agency not found");
        }

        List<Agent> agents = agentRepository.findAll().stream().filter(ag -> ag.getAgency().getId().equals(id)).collect(Collectors.toList());
        List<AgentDto> mappedAgents = agents.stream().map(agent -> mapper.mapToDto(agent)).collect(Collectors.toList());

        return mappedAgents;
    }

This is the security configuration:

@Configuration
@EnableWebSecurity
@Profile("h2")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider auth = new DaoAuthenticationProvider();

        auth.setUserDetailsService(userService);
        auth.setPasswordEncoder(passwordEncoder());
        return auth;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web
                .ignoring()
                .antMatchers("/h2-console/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception{
       http.authorizeRequests().antMatchers(
               "/register/**"
       ).permitAll().anyRequest().authenticated()
               .and().formLogin().loginPage("/login")
               .permitAll()
               .and().logout().invalidateHttpSession(true).clearAuthentication(true)
               .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
               .logoutSuccessUrl("/login?logout").permitAll();
    }
}

The error I'm getting debugging:

2022-04-11 09:24:56.781  WARN 20968 --- [           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 'userController': Unsatisfied dependency expressed through field 'passwordEncoder'; 
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 
'org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' available: expected at least 1 bean which qualifies as autowire candidate. 
Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

You have annotated your AgentService with @MockBean so when it is supposed to be called during the test only a mock is called and the result is null resulting in the page returned by your endpoint with listAgents = null. Therefore the 200 in both cases. You can either include your AgentService and your AgentMapper in the applicationContext of your test and annotate them with @Autowired or you could specify the behavior of the mocks but since you want to use h2 I guess you should go with the first one. Additionally you could also define an exception handler for the case when your exception is thrown to explicitly return a 404

    @ControllerAdvice
    public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

      @ExceptionHandler(AgencyNotFoundException.class)
      public ResponseEntity<Object> handleAgencyNotFound(AgencyNotFoundException exception, WebRequest request) {
        return handleExceptionInternal(exception, exception.getMessage(),
            new HttpHeaders(), HttpStatus.NOT_FOUND, request);
      }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM