This class is in the top of my tests hierarchy:
#TestPropertySource("/test.properties")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public abstract class ApplicationAbstractTest {
}
And few more test classes:
#WebAppConfiguration
#ActiveProfiles("mysql")
abstract public class AbstractControllerTest extends ApplicationAbstractTest {
protected MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#PostConstruct
private void postConstruct() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.build();
}
}
JsonUserServiceTest:
#ActiveProfiles("json")
public class JsonUserServiceTest extends ApplicationAbstractTest {
#Before
public void setUp() throws Exception {
...
}
}
ContactControllerTest:
public class ContactControllerTest extends AbstractControllerTest {
#Test
public void testGet() throws Exception {
mockMvc.perform(get("/update-" + ID + "-contact")
.with(userAuth(USER)))
// .andExpect(status().isOk())
.andDo(print())
.andExpect(view().name("details"))
.andExpect(forwardedUrl("/WEB-INF/jsp/details.jsp"));
}
}
So, when I run ContactControllerTest along - it is successfull, and print method shows me:
Handler:
Type = com.telecom.web.ContactController
Method = public java.lang.String com.myApp.web.ContactController.details(java.lang.Integer,org.springframework.ui.ModelMap)
But when I run all tests, so JsonUserServiceTest runs first, ContactControllerTest fails. And print shows:
Handler:
Type = null
...
java.lang.AssertionError: No ModelAndView found
What is wrong in configuration? Or how troubleshoot it?
UPD:
at the same time, test like this, allways works fine:
public class UserControllerTest extends AbstractControllerTest {
#Test
public void testRegister() throws Exception {
mockMvc.perform(get("/register"))
.andDo(print())
.andExpect(view().name("profile"))
.andExpect(forwardedUrl("/WEB-INF/jsp/profile.jsp"));
}
}
UPD:
There is controller's method I'm testing:
#GetMapping("/update-{id}-contact")
public String details(#PathVariable Integer id, ModelMap model) {
Integer userId = AuthorizedUser.id();
LOG.info("get contact {} for User {}", id, userId);
Contact contact = service.get(id, userId);
model.addAttribute("contact", contact);
return "details";
}
I also have such bean:
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
UPD: I've tried configure mockMvc in separate class:
#Configuration
public class TestConfig {
#Autowired
private WebApplicationContext webApplicationContext;
#Bean
public MockMvc mockMvc() {
return MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.build();
}
}
And added it here:
#WebAppConfiguration
#ContextConfiguration(classes = {TestConfig.class})
#ActiveProfiles("mysql")
abstract public class AbstractControllerTest extends ApplicationAbstractTest {
but I've received:
java.lang.IllegalStateException: springSecurityFilterChain cannot be
null. Ensure a Bean with the name springSecurityFilterChain
implementing Filter is present or inject the Filter to be used.
The WARN message doesn't cause the test cases to fail. It just says that Entity manager factory is registered twice. This will only be an issue if you cluster your application using the same Entity Manager Factory. For test case run it is not a cause for concern.
The root cause of the testcase failure is in these two lines
.andExpect(view().name("details"))
.andExpect(forwardedUrl("/WEB-INF/jsp/details.jsp"));
Please check if the project has a view named "details" and the forwardded url is "/WEB-INF/jsp/details.jsp"
Update
Could you please try this
#Configuration
public class TestConfig {
#Autowired
private Filter springSecurityFilterChain;
#Autowired
private WebApplicationContext webApplicationContext;
#Bean
public MockMvc mockMvc() {
return MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.apply(springSecurityFilterChain)
.build();
}
}
Create a configuration file that will initialize mocking objects for your test cases. And put at all test case classes.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class})
It will initialize all your mocking objects only once and cached after that and reused for all test cases.
Or if you don't want to use mocking configuration, you can directly
pass the actual application configuration to ContextConfiguration as
below
For annotation based application configuration (here AppConfig and AppConfig2 are your configuration class)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {AppConfig.class, AppConfig2.class})
For xml based application configuration (here appConfig.xml and appConfig2.xml are your configuration files)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:pathTo/appConfig.xml","classpath:pathTo/appConfig2.xml"})
Reference : JUnit + Spring integration example
Related
I try to test my spring app but encounter following problem:
In "normal mode"(mvn spring-boot:run) the app starts as expected and adapterConfig gets set and is NOT NULL. When I start my testclass to test the MVC, adapterConfig does not get set. Spring ignores the whole config class.
test:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = StudentController.class)
public class StudentControllerTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private StudentService service;
#MockBean
private StudentRepository repository;
#Test
public void shouldReturnABC() throws Exception{
MvcResult result = this.mockMvc.perform(get("/students/abc")).andReturn();
}
}
controller:
#RestController
#RequestMapping("/students")
#PermitAll
public class StudentController {
#Autowired
StudentService studentService;
//get
#GetMapping("/abc")
public String abc (){
return "abc";
}
config:
#Configuration
public class SpringBootKeycloakConfigResolver implements KeycloakConfigResolver {
private KeycloakDeployment keycloakDeployment;
private AdapterConfig adapterConfig;
#Autowired
public SpringBootKeycloakConfigResolver(AdapterConfig adapterConfig) {
this.adapterConfig = adapterConfig;
}
#Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
if (keycloakDeployment != null) {
return keycloakDeployment;
}
keycloakDeployment = KeycloakDeploymentBuilder.build(adapterConfig);
return keycloakDeployment;
}
}
adapterConfig is null when hitting the test but gets set & created when hitting it the normal way, any idea?
Using #WebMvcTest, the container will inject only components related to Spring MVC (#Controller, #ControllerAdvice, etc.) not the full configuration use #SpringBootTest with #AutoConfigureMockMvc instead.
Spring Boot Javadoc
Keycloak's AutoConfiguration is not included by #WebMvcTest.
You could
Include it manually via #Import(org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration.class)
Or use #SpringBootTest
with spring boot 2.5 i had I had to import KeycloakAutoConfiguration into my test.
#WebMvcTest(value = ApplicationController.class, properties = "spring.profiles.active:test")
#Import(KeycloakAutoConfiguration.class)
public class WebLayerTest {
// ... test code ....
}
I confused with configuration for unit test:
This's my test class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class MyTest {
private MockMvc mockMvc;
#Autowired
private MyController myController;
#Before
public void setUp() {
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
#Test
public void test() {
}
#Configuration
static class Config {
#Bean
MyController myController() {
return new MyController();
}
}
}
When I run it, I get:
java.lang.IllegalStateException: springSecurityFilterChain cannot be
null. Ensure a Bean with the name springSecurityFilterChain
implementing Filter is present or inject the Filter to be used.
How to configure it properly?
I have created the Home Controller below. This controller fetches the 5 dummy posts I have created in the "PostRepository" class through PostService class.
#Controller
public class HomeController {
#Autowired
PostService postService;
#RequestMapping("/")
public String getHome(Model model){
model.addAttribute("Post", postService);
return "home";
}
}
I have implemented the following test..
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebConfig.class})
#WebAppConfiguration
public class ControllerTest {
#Test //Test the Home Controller
public void TestHomePage() throws Exception{
HomeController homeController = new HomeController();
MockMvc mockMvc = standaloneSetup(homeController).build();
mockMvc.perform(get("/"))
.andExpect(view().name("home"))
.andExpect(model().attributeDoesNotExist("Post"));
}
}
The test has successfully passed. But the attribute should exist.
You are mixing two incompatible features of Spring's testing support.
If you instantiate the controller within the test, you need to use MockMvcBuilders.standaloneSetup().
If you are using the Spring TestContext Framework (i.e., #ContextConfiguration, etc.), then you need to use MockMvcBuilders.webAppContextSetup().
Thus, the following is the appropriate configuration for your test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = WebConfig.class)
#WebAppConfiguration
public class ControllerTest {
#Autowired
WebApplicationContext wac;
#Autowired
PostService postService;
#Test
public void TestHomePage2() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
mockMvc.perform(get("/"))
.andExpect(view().name("home"))
.andExpect(model().attribute("Post",postService));
}
}
Regards,
Sam (author of the Spring TestContext Framework)
If that's the complete code, then you are missing
#RunWith(SpringJUnit4ClassRunner.class)
I wanted to get json response for exception in mockito unit testing.
This is my Application configuration file.
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.spring")
public class AppConfig extends WebMvcConfigurerAdapter{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
This is my exception class for existing user:
public class ConflictException extends RuntimeException{
public ConflictException() {
}
public ConflictException(String message) {
super(message);
}
}
This is my global exception controller class annotated with #ControllerAdvice.
#EnableWebMvc
#ControllerAdvice
public class GlobalExceptionHandlerController extends ResponseEntityExceptionHandler{
public GlobalExceptionHandlerController() {
super();
}
#ExceptionHandler(ConflictException.class)
public ResponseEntity<Map<String, Object>> handleException(
Exception exception, HttpServletRequest request) {
ExceptionAttributes exceptionAttributes = new DefaultExceptionAttributes();
Map<String, Object> responseBody = exceptionAttributes.getExceptionAttributes(exception, request, HttpStatus.CONFLICT);
return new ResponseEntity<Map<String,Object>>(responseBody, HttpStatus.CONFLICT);
}
}
Now, this is my controller test class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#EnableWebMvc
#ActiveProfiles("Test")
#ContextConfiguration(classes={AppConfig.class})
public class UserControllerTest {
#InjectMocks
private UserController userController;
#Mock
private UserService service;
private MockMvc mockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
final ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = new ExceptionHandlerExceptionResolver();
//here we need to setup a dummy application context that only registers the GlobalControllerExceptionHandler
final StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerBeanDefinition("advice", new RootBeanDefinition(GlobalExceptionHandlerController.class, null, null));
//set the application context of the resolver to the dummy application context we just created
exceptionHandlerExceptionResolver.setApplicationContext(applicationContext);
//needed in order to force the exception resolver to update it's internal caches
exceptionHandlerExceptionResolver.afterPropertiesSet();
mockMvc = MockMvcBuilders.standaloneSetup(userController).setHandlerExceptionResolvers(exceptionHandlerExceptionResolver).build();
}
#Test
public void createUserExistsTest() throws Exception {
when(service.createUser(any(User.class))).thenThrow(new ConflictException("User exists."));
mockMvc.perform(post("/user")
.content("{\"username\": \"bimal\", \"password\": \"check\", \"email\": \"test#gmail.com\", \"maxCaloriesPerDay\": \"1000\"}")
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isConflict());
}
}
When I run my test method, I get following error:
ERROR:
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Failed to invoke #ExceptionHandler method: public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> com.spring.app.exception.GlobalExceptionHandlerController.handleException(java.lang.Exception,javax.servlet.http.HttpServletRequest)
java.lang.IllegalArgumentException: No converter found for return value of type: class java.util.LinkedHashMap
at org.springframework.util.Assert.isTrue(Assert.java:68)
How can I resolve this error? Exception is thrown but I cannot convert and use it.
I'm over a year too late on this one, but for future reference this solved the problem for me.
The problem is that your exception resolver is not aware of any message converters, since you are providing it with a static application context.
It can be solved by the following code, directly below exceptionHandlerExceptionResolver.setApplicationContext(applicationContext):
exceptionHandlerExceptionResolver.setMessageConverters(
Collections.singletonList(new MappingJackson2HttpMessageConverter(new ObjectMapper()))
);
It is being handled. This error refers to no HttpMessageConverter for the response entity type. Add a JacksonHttpMessageConverter to the spring context.
Override this method from WebMvcConfigurerAdapter in your AppConfig:
#Override
public void configureMessageConverters(List<HttpMessageConverter> converters) {
messageConverters.add(new MappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
}
I have a problem I'm not able to solve. I have searched on the internet and on Stackoverflow but could not find how to solve the problem.
I want to test a Spring MVC Handler interceptor. This interceptor has a "session" scope bean as a dependency.
I tried to reduce the code as much as possible. Here is the code:
The src part :
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "..." })
public class SpringMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry interceptorRegistry) {
interceptorRegistry.addInterceptor(initializeUserLanguageHandler());
}
#Bean
public InitializeUserLanguageHandler initializeUserLanguageHandler() {
return new InitializeUserLanguageHandler();
}
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public SessionBean sessionBean() {
return new SessionBean();
}
}
#Component
public class InitializeUserLanguageHandler extends AbstractHandlerInterceptor {
#Autowired
private SessionBean sessionBean;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (sessionBean.getLanguage() == null) {
sessionBean.setLanguage(getUserLanguage());
}
return true;
}
}
The test part:
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringMvcConfiguration.class)
public class BaseSpringMvcIntegrationTest {
#Resource
protected WebApplicationContext webApplicationContext;
protected MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}
public class InitializeUserLanguageHandlerTest extends BaseSpringMvcIntegrationTest {
#Autowired
private SessionBean sessionBean;
#Autowired
private MockHttpSession mockHttpSession;
#Test
public void testLanguageIsInitializedOnlyOnce() throws Exception {
MockHttpSession mocksession = new MockHttpSession();
// It is null, this is because the interceptor has not been called yet
assertEquals(null, sessionBean.getLanguage());
// This line will call the interceptor and set language to "nl"
mockMvc.perform(get("/").session(mocksession).principal(getUser("nl")));
// It is null, but I expect it to be "nl"
assertEquals(null, sessionBean.getLanguage());
// Let's try again
mockMvc.perform(get("/").session(mocksession).principal(getUser("fr")));
// It is null, but I expect it to be "nl"
assertEquals(null, sessionBean.getLanguage());
}
}
You can see in the test class "InitializeUserLanguageHandlerTest" that I have some assertions.
The first time I call:
mockMvc.perform(get("/").session(mocksession).principal(getUser()));
The code in the interceptor is executed and language is set to "nl". Therefore, in my test, I would have expected that sessionBean.getLanguage() would return me "nl", but it is not. I don't understand why.
So I'm calling the perform again, the interceptor code is executed again, and calling sessionBean.getLanguage() returns "nl".
It seems I've two SessionBean instances, one in my test and the other in the source. But when I look at the SessionBean variable in Eclipse in Debug mode, they have the same ID.
If I change the "session" scope to "application" scope, it is working properly.
Can somebody help me ?
Thank you.
Here is one way to solve the problem, not sure it is the best though.
#ContextConfiguration(classes = {SpringMvcConfiguration.class, InitializeUserLanguageHandlerTest.BeanConfig.class})
public class InitializeUserLanguageHandlerTest extends BaseSpringMvcIntegrationTest {
#Configuration
public static class BeanConfig {
#Bean(name = "sessionBean")
public SessionBean sessionBean() {
return new SessionBean();
}
}
...
}