I'm using JHipster 6.2.0 and I want to create a WebMvcTest for a RestController. When I run the test, the application context does not load and I get:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.mytest.security.jwt.TokenProvider' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I'm using junit5 and spring boot 2.1.7
I tried adding #MockBean TokenProvider tokenProvider, and then the next missing dependency is CorsFilter. Mocked that but then I get a IllegalArgumentException because the mocked CorsFilter does not have a filter name defined. I thought about disabling the security filters, but I would like to keep the test configurations as vanilla as possible, utilizing the WebMvcTest default configurations.
I reproduced the issue with this simple example.
package com.example.mytest.web.rest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api")
public class MyController {
#GetMapping("/my")
public MyResponse get() {
MyResponse myResponse = new MyResponse();
myResponse.text = "test";
return myResponse;
}
static class MyResponse {
String text;
}
}
package com.example.mytest.web.rest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.junit.jupiter.api.Assertions.*;
#WebMvcTest(MyController.class)
class MyControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
void get() {
// doesn't run, due to NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.mytest.security.jwt.TokenProvider' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
}
}
I expect that I should be able to run a GET mockMvc test without having to mock a CorsFilter name. This mocking would need to be done in every WebMvcTest and is very repetitive. I don't want to run a full #SpringBootTest for every RestController. All the generated RestController tests are #SpringBootTests and run in a full application context.
Some similar questions:
Spring Boot 2.1 - #WebMvcTest without Spring Security Auto-Configuration
Security configuration on Spring Boot 2.1 - How to keep the default instead of my custom for tests?
I have a Spring MVC test:
#RunWith(SpringRunner.class)
#WebMvcTest
#Configuration
public class InventoryControllerIntegrationTest {
#MockBean
private InventoryService inventoryService;
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnInventory() throws Exception {
this.mockMvc.perform(get("/inventory")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World")));
}
}
And a corresponding controller.
After running the test I get the exception:
2018-01-27 21:28:25.099 ERROR 12470 --- [ main] o.s.test.context.TestContextManager : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener#6df37f7a] to prepare test instance [com.video.rental.store.videorentalstore.inventory.InventoryControllerIntegrationTest#21d3d6ec]
java.lang.IllegalStateException: The #PropertyMapping annotation #WebMvcTest cannot be used in combination with the #Component annotation #Configuration
at org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer$PropertyMappingCheckBeanPostProcessor.postProcessBeforeInitialization(PropertyMappingContextCustomizer.java:99) ~[spring-boot-test-autoconfigure-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:409) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
How do I go around this and add the mock bean inventoryService?
I have few working code to set up MockMVc in different ways with the new Spring Boot 1.4 #WebMvcTest. I understand the standaloneSetup approach. What I want to know is the difference between setting up MockMvc through WebApplicationContext and by autowiring MockMvc.
Code Snippet 1: MockMvc through WebApplicationContext Setup
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ProductController.class)
public class ProductControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private ProductService productServiceMock;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testShowProduct() throws Exception {
Product product1 = new Product();
/*Code to initialize product1*/
when(productServiceMock.getProductById(1)).thenReturn(product1);
MvcResult result = mockMvc.perform(get("/product/{id}/", 1))
.andExpect(status().isOk())
/*Other expectations*/
.andReturn();
}
}
As per WebMvcTest API documentation, By default, tests annotated with #WebMvcTest will also auto-configure Spring Security and MockMvc. So, I expected a 401 Unauthorized status code here, but the test passes with a 200 status code.
Next, I tried auto wiring MockMvc, but the test fails with 401 Unauthorized status code, unless I add #AutoConfigureMockMvc(secure=false) or update the #WebMvcTest annotation to disable security:
#WebMvcTest(controllers = IndexController.class, secure = false)
Following is the code that passes ONLY AFTER explicitly disabling security.
Code Snippet 2: MockMvc through Autowiring
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ProductController.class)
#AutoConfigureMockMvc(secure=false)
public class ProductControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private ProductService productServiceMock;
#Test
public void testShowProduct() throws Exception {
Product product1 = new Product();
/*Code to initialize product1*/
when(productServiceMock.getProductById(1)).thenReturn(product1);
MvcResult result = mockMvc.perform(get("/product/{id}/", 1))
.andExpect(status().isOk())
/*Other expectations*/
.andReturn();
}
}
So my questions are:
Why didn't Code snippet 1 report a a 401 Unauthorized status code error while auto wiring MockMvc did. Also reiterating what the official doc says By default, tests annotated with #WebMvcTest will also auto-configure Spring Security and MockMvc. But, in this case it appears #WebMvcTest has nothing to do with auto-configuring Spring Security (Because Code Snippet 1 passes without any 401 error). It finally boils down to how I set up the MockMvc. Am I correct here?
What are the differences/objectives between/of both the approaches?
How does disabling security via #AutoConfigureMockMvc(secure=false) differs from doing through #WebMvcTest(controllers = IndexController.class, secure = false). Which one is the preferred approached or when (or where) to use them?
I also come across similar problem. #WebMvcTest auto configures Spring Security with basic auth but I have a WebSecurityConfig class that extends WebSecurityConfigurerAdapter. In this class I disabled basic auth and configured token base security. That means WebSecurityConfig class is not used to configure Spring Security.
To resolve the problem, I added #ContextConfiguration to my unit test class and added mocks of dependencies of WebSecurityConfig class.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = CategoryRestService.class)
#ContextConfiguration(classes = {MjApplication.class, WebSecurityConfig.class})
public class CategoryRestServiceTest {
#MockBean
private CategoryRepository repository;
#MockBean
CurrentUserDetailsService currentUserDetailsService;
#MockBean
TokenAuthProvider tokenAuthProvider;
#Autowired
MockMvc mockMvc;
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Test
public void getCategories() throws Exception {
Category category1 = new Category();
category1.setName("Test Category 1");
category1.setId(1L);
Category category2 = new Category();
category2.setName("Test Category 2");
category2.setId(2L);
List<Category> categoryList = new ArrayList<Category>();
categoryList.add(category1);
categoryList.add(category2);
given(this.repository.findAll())
.willReturn(categoryList);
mockMvc.perform(get("/public/rest/category"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$[0].id", is(1)))
.andExpect(jsonPath("$[0].name", is("Test Category 1")))
.andExpect(jsonPath("$[1].id", is(2)))
.andExpect(jsonPath("$[1].name", is("Test Category 2")));
}
}
According to this issue in github
https://github.com/spring-projects/spring-boot/issues/5476
#WebMvcTest auto configures by default, a basic auth when spring-security-test is in the class path
Answering your questions:
In the code snippet 1, you not injected the MockMvc in your test class, you should add .apply(springSecurity()) in the builder on the setup method, so spring would use the basic configuration (not your custom security configuration if you have one)
both approaches does basically the same thing, the difference is that the second comes with the basic auth already in the MockMvc, that is why you have to use the secure=false
From the documentation:
By default, tests annotated with #WebMvcTest will also auto-configure
Spring Security and MockMvc (include support for HtmlUnit WebClient
and Selenium WebDriver). For more fine-grained control of MockMVC the
#AutoConfigureMockMvc annotation can be used.
I'm not sure this is directly related, but there is an outstanding bug where, if using spring boot and #WebMvcTest, your custom #EnableWebSecurity config class will be ignored. A couple workarounds are mentioned in the bug report. I'm using:
#WebMvcTest(includeFilters = #Filter(classes = EnableWebSecurity.class))
I don't know if this is correct way or not but I could disable configurations class by using like below
#WebMvcTest(ProductController.class)
#ContextConfiguration(classes = ProductController.class)
public class ProductControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private ProductService productServiceMock;
I have a class derived from AbstractPreAuthenticatedProcessingFilter in my Spring Security Filter Chain. The purpose of this filter is to massage role data left in a special Principal object by a corporate authentication service into a Collection so SpringSecurity can use them.
However, I cannot get past this exception:
Caused by: java.lang.IllegalArgumentException: An AuthenticationManager must be set
at org.springframework.util.Assert.notNull(Assert.java:112) ~[spring-core-4.1.6.RELEASE.jar:4.1.6.RELEASE]
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.afterPropertiesSet(AbstractPreAuthenticatedProcessingFilter.java:97) ~[spring-security-web-4.0.1.RELEASE.jar:4.0.1.RELEASE]
I am using Java config, not XML config. My code following the example of How To Inject AuthenticationManager using Java Configuration in a Custom Filter is as follows:
the security configurer adaptor
#Configuration
#EnableWebSecurity
public class MyWebSecurityAdaptor extends WebSecurityConfigurerAdapter {
...
#Bean(name = "myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
The filter class itself:
#Component
public class MyPreauthFilter extends AbstractPreAuthenticatedProcessingFilter {
...
#Autowired
#Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
}
If instead of the code in Item 1 above, I try the following:
#Autowired
#Override
protected AuthenticationManager authenticationManager() throws Exception
{
// TODO Auto-generated method stub
return super.authenticationManager();
}
Then the error changes.
It then becomes:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.authentication.AuthenticationManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate}
I guess that makes sense, this way does not define a bean. But then why didn't the original way, which DID define a bean, fail?
Instead of adding 'myAuthernticationManager' to the WebSecurityConfigurerAdapter class. Add this directly to you filter class and autowire it.
#Autowired
#Override
public void setMyAuthenticationManager(MyAuthenticationManager myAuthenticationManager) {
this.myAuthenticationManager = myAuthenticationManager;
super.setAuthenticationManager(this.myAuthenticationManager);
}
Remove all the code related to myAuthenticationManager from your WebSecurityConfigurerAdapter.
I am trying to autowiring my bean inside contextInitialized() method of my custom Context Listener class, but it is not working.
public class CustomContextListener extends ContextLoaderListener {
#Autowired
private MyBeanClass bean;
#Override
public void contextInitialized(javax.servlet.ServletContextEvent event) {
super.contextInitialized(event);
//call to my method.
bean.mymethod();
}
But here it is not getting autowired, i am getting null object for MyBeanClass reference.
How to autowire a class at the time of tomcat startup.
Please provide me alternate places where i can execute some code using autowiring at the time of server startup (here tomcat).
I would suggest to use the WebApplicationContext method to find the bean and then invoke.
WebApplicationContext servletContext = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());
bean = (MyBeanClass) servletContext.getBean("myBeanClass");
bean.yourMethod();
More systematic to use... :)