I have method:
#PreAuthorize("#securityManager.check(#uuid)")
#GetMapping(path = URL_SUBMISSION_ID)
#ControllerLogging
public Object showSomething(#PathVariable(value = "submissionId") UUID submissionId) {
return getData();
}
It works without problem, but I can't test it, because get error:
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
java.lang.IllegalArgumentException: Failed to evaluate expression
'#securityManager.check(#uuid)'
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() {
// Here some code with mvc mock
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
static class Config {
#Bean
MyController myController() {
return new MyController();
}
}
}
What do I miss?
The problem has to do with specifying stating "uuid" in the PreAuthorize method.
It should be rewritten to
#PreAuthorize("#securityManager.check(#submissionId)")
Related
Can someone point me to what could be wrong in below code. It is a boot spring 2.6.7 application. When test profile is running, it throws error for all tests like below.
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
#AutoConfigureMockMvc
#SpringBootTest(classes = some.class)
#ActiveProfiles("test")
public class someTest {
#Autowired
private MockMvc mvc;
#Autowired
private WebApplicationContext webAppContext;
#MockBean
private SomeBean someBean;
#SpyBean
private SomeSpyBean someSpyBean;
#BeforeEach
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(webAppContext)
.apply(springSecurity())
.build();
}
#Test
public void SomeTest1() throws Exception {
String text = "text1";
when(someBean.findStuff(text).thenReturn(Optional.of(new Thingie()));
mvc.perform(multipart("/api/somepath/")
.andExpect(status().isNotFound());
verify(someSpyBean).doStuff();
}
#Test
public void SomeTest2() throws Exception {
String text = "text2";
when(someBean.findStuff(text).thenReturn(Optional.of(new Thingie()));
mvc.perform(multipart("/api/somepath/")
.andExpect(status().isFound());
verify(someSpyBean).doStuff();
}
}
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?
The status I expect when testing is 200, but I get 404 instead right now.
I am fairly new to Mockido, so if there is something simple that I am missing. Please let me know.
I have created a POST request in my controller that takes a List of Long objects. If no exception happens, returns OK for status:
#PostMapping(path = "/postlist")
public ResponseEntity<Void> updateAllInList(#RequestBody List<Long> ids) {
try {
// method from ControllerService.java here using ids
return ResponseEntity.status(HttpStatus.OK).body(null);
} catch (InvalidContentException e) {
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(null);
}
When I POST using a REST client, I am getting the correct results. The raw payloads I POST are like this:
[
2, 1
]
However, the unit test is giving me a 404.
The way I created the Test class is like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextHierarchy({ #ContextConfiguration(classes = RootConfiguration.class), #ContextConfiguration(classes = WebConfiguration.class) })
#Category(UnitTest.class)
public class ControllerTest {
private static final String POST_REQUEST = "[ 2, 1 ]";
#Autowired private WebApplicationContext webApplicationContext;
#Autowired private ControllerService controllerService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
doNothing().when(this.controllerService).updateAllInList(anyList());
doThrow(InvalidContentException.class).when(this.controllerService).updateAllInList(null);
}
#Test
public void updateList() throws Exception {
this.mockMvc.perform(post("http://testhost/api/configuration/postlist").contentType(MediaType.APPLICATION_JSON_UTF8).content(POST_REQUEST))
.andExpect(status().isOk());
}
#Configuration
static class RootConfiguration {
#Bean
public ControllerService ControllerService() {
return Mockito.mock(ControllerService.class);
}
}
#Configuration
#EnableWebMvc
static class WebConfiguration extends WebMvcConfigurerAdapter {
#Autowired
private ControllerService controllerService;
#Bean
public Controller controller() {
return new Controller(controllerService);
}
}
}
My theory is that in my test class I am plugging in the wrong content. But why can't we plug in the same content as the one we use from real POST raw payload?
Thanks.
When you use MockMvc, you want to trigger the mapping of your controller, not the HTTP server.
Instead of mockMvc.perform(post("http://testhost/api/configuration/postlist")...
Try mockMvc.perform(post("/configuration/postlist")...
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
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);
}