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.