Hi,
I have 2 integration tests passing with Spring Boot (2.0.X, probably with version 1.X as well) but when I use the latest version 2.1.0, the first one is failing, it returns 404 instead of 200...
Any idea? I could not find any breaking change about this in the release note.
Thanks,
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MockMvcExampleTests {
@Autowired
private MockMvc mvc;
@Test
public void testHomeUrl() throws Exception {
this.mvc.perform(get("/home")).andExpect(status().isOk())
.andExpect(content().string("Hello World"));
}
@Test
public void testAdminHomeUrl() throws Exception {
this.mvc.perform(get("/admin/home")).andExpect(status().isUnauthorized());
}
@Configuration
public static class MyTestConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/admin/**").hasRole("USER").and()
.httpBasic();
}
}
@Controller
public static class TestController {
@RequestMapping(path = "/home")
public ResponseEntity<String> home() {
return ResponseEntity.ok("Hello World");
}
@RequestMapping(path = "/admin/home")
public ResponseEntity<String> adminHome() {
return ResponseEntity.ok("Hello World Secured");
}
}
}
Comment From: wilkinsona
In Boot 2.1, your TestController isn't part of the application context but in Boot 2.0 it is. When the controller's missing, the test for /admin/home works due to your security configuration and it being rejected with a 401. The test for /home fails as the controller isn't found so there's nothing in the context to handle the request.
The change in behaviour is due to a change in Spring Framework. If you drop back to Boot 2.0.6 but override the Framework version to 5.1, you'll see the same failure. I believe this is due to the changes made in SPR-17206.
You can cause TestController to be picked up by annotating it with @TestConfiguration.
Comment From: fabianpiau
Yes, it is working now. Thanks a lot!
Comment From: singhpradeepkumar
I was facing same problem with Spring Boot 2.1 but @TestConfiguration does not seems to work in my case.
Please find the working code below.
@WebMvcTest(ReportController.class)
@ComponentScan(basePackages = "com.pkg")
public class ReportControllerTest {
@Autowired
private MockMvc mockMvc;
}
After adding @ComponentScan(basePackages = "com.pkg") issue has been resolved.
Comment From: rougou
@singhpradeepkumar
I'm also facing a similar issue where @WebMvcTest(MyController.class) isn't enough to wire in MyController.class - which is what I think it's supposed to do - causing the 404. Adding @Import(MyController.class) makes everything work. I'll see if I can create a sample app reproducing this behavior.
Comment From: morenoian
In my case I had to combine some of the above steps to finally get my tests working:
On my controller to be tested:
@RestController
@RequestMapping(value = "/rest")
@TestConfiguration
public class SomeControllerImpl implements SomeController {
On my test class:
@RunWith(SpringRunner.class)
@WebMvcTest(controllers = SomeControllerImpl.class)
@Import(SomeControllerImpl.class)
public class SomeControllerImplTest {
Comment From: 4javier
I got to confirm that the problem is still there.
Spring 5.13.17
Spring-Boot 2.6.5
This simple test executed with JUnit5 (Jupiter) fails with 404 if I strip the BrandController.class from @Import annotation's array.
Adding that one, test passes. (No need for @TestConfiguration on BrandController class.)
@Import({SecurityConfig.class, BrandController.class})
@ActiveProfiles("RESTControllersTest")
@WebMvcTest(BrandController.class)
public class BrandControllerTest {
@Configuration
public static class TestContext{
@Bean
public static PropertySourcesPlaceholderConfigurer properties(){
return new PropertySourcesPlaceholderConfigurer();
}
}
@MockBean
private BrandService brandService;
@Autowired
private MockMvc mockMvc;
@Test
void listBrands() throws Exception {
mockMvc.perform(get("/brand/search"))
.andExpect(status().isOk());
}
}
The change in behaviour is due to a change in Spring Framework. If you drop back to Boot 2.0.6 but override the Framework version to 5.1, you'll see the same failure. I believe this is due to the changes made in SPR-17206.
I read that ticket you linked @wilkinsona , but in every docs I read about @WebMvcTest I see confirmation that
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).
That should explicitly means that dropping of lite beans scanning (that's what I understood from that ticket, but far from be an expert about the matter) should not interfere with this annotation @Controllers auto configuration capability.
Comment From: wilkinsona
@4javier, the advice in this issue only applies to a controller that is nested within the test class. Looking at BrandControllerTest, you are not in that situation so the advice does not apply.
Your use of @Configuration on TestContext may be a problem but I cannot tell for certain as your example is incomplete. If you would like us to spend some more time investigating, please open a new issue providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to the issue.
Comment From: 4javier
@wilkinsona
Thanks a lot.
It looks like the root problem was indeed the @Configuration annotated inner class.
Once I replaced it with @ImportAutoConfiguration(PropertyPlaceholderAutoConfiguration.class) on BrandControllerTest as per your suggestion on Gitter channel, I've been able to remove BrandController.class form @Import array.
Comment From: Robsonmendes1987
Estou com problema parecido, i,plementei o teste e retorna o texto "/ AssertionError: expected status to have been called with arguments 201 404 201 /" Meu codigo it('inserindo um produto', async function () { const res = {}; const req = { params: { productId: 1 }, body: {} }; // const id = req.params
res.status = sinon.stub().returns(res); res.json = sinon.stub().returns(); sinon.stub(productService, 'insertProducts') .resolves({ type: null, meesage: productFromDb })
await productControler.getAllProductById(req, res);
expect(res.status).to.have.calledWith(201);
expect(res.json).to.be.have.calledWith(productFromDb[0]);
})