I am using Spring for MVC tests
Here is my test class
@RunWith(SpringRunner.class) @WebMvcTest public class ITIndexController { @Autowired WebApplicationContext context; MockMvc mockMvc; @MockBean UserRegistrationApplicationService userRegistrationApplicationService; @Before public void setUp() { this.mockMvc = MockMvcBuilders .webAppContextSetup(context) .apply(springSecurity()) .build(); } @Test public void should_render_index() throws Exception { mockMvc.perform(get("/")) .andExpect(status().isOk()) .andExpect(view().name("index")) .andExpect(content().string(containsString("Login"))); } }
Here is the MVC config
@Configuration @EnableWebMvc public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/login/form").setViewName("login"); } }
Here is the security config
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired @Qualifier("customUserDetailsService") UserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/resources/**", "/signup", "/signup/form", "/").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login/form").permitAll().loginProcessingUrl("/login").permitAll() .and() .logout().logoutSuccessUrl("/login/form?logout").permitAll() .and() .csrf().disable(); } @Autowired public void configureGlobalFromDatabase(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } }
When I run my test it fails with the message:
java.lang.AssertionError: Status expected:<200> but was:<401> at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:54) at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:81) at org.springframework.test.web.servlet.result.StatusResultMatchers$10.match(StatusResultMatchers.java:664) at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171) at com.marco.nutri.integration.web.controller.ITIndexController.should_render_index(ITIndexController.java:46) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
I understand that it fails due to the fact that the url is protected with spring security, but when I run my application I can access that url even without being authenticated.
Am I doing something wrong?
If you want to ignore a test method, use @Ignore along with @Test annotation. If you want to ignore all the tests of class, use @Ignore annotation at the class level.
Spring Boot provides a @SpringBootTest annotation, which can be used as an alternative to the standard spring-test @ContextConfiguration annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests through SpringApplication .
I found the answer
Spring docs says that:
@WebMvcTest will auto-configure the Spring MVC infrastructure and limit scanned beans to @Controller, @ControllerAdvice, @JsonComponent, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver. Regular @Component beans will not be scanned when using this annotation.
And according to this issue in github:
https://github.com/spring-projects/spring-boot/issues/5476
The @WebMvcTest by default auto configure spring security if spring-security-test is present in the class path (which in my case is).
So since WebSecurityConfigurer classes aren't picked, the default security was being auto configured, that is the motive I was receiving the 401 in url's that was not secured in my security configuration. Spring security default auto configuration protects all url's with basic authentication.
What I did to solve the problem was to annotate the class with @ContextConfiguration, and @MockBean like it is described in the documentation:
Often @WebMvcTest will be limited to a single controller and used in combination with @MockBean to provide mock implementations for required collaborators.
And here is the test class
@RunWith(SpringRunner.class) @WebMvcTest @ContextConfiguration(classes={Application.class, MvcConfig.class, SecurityConfig.class}) public class ITIndex { @Autowired WebApplicationContext context; MockMvc mockMvc; @MockBean UserRegistrationApplicationService userRegistrationApplicationService; @MockBean UserDetailsService userDetailsService; @Before public void setUp() { this.mockMvc = MockMvcBuilders .webAppContextSetup(context) .apply(springSecurity()) .build(); } @Test public void should_render_index() throws Exception { mockMvc.perform(get("/")) .andExpect(status().isOk()) .andExpect(view().name("index")) .andExpect(content().string(containsString("Login"))); } }
Application, MvcConfig and SecurityConfig are all my configuration classes
Not sure if this was available when the original question was asked, but if truly not wanting to test the security portion of a web request (which seems reasonable if the endpoint is known to be unsecure), then I think this could be done simply by using the secure
attribute of the @WebMvcTest
annotation (it defaults to true
so setting it to false
should disable the auto-configuration of Spring Security's MockMvc support):
@WebMvcTest(secure = false)
More info available in the javadocs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With