MockMVC with Spring Boot Security including Filter to verify model and view - spring-boot

Is it not possible to verify the view name/modelandview using mockMvc with spring security?
I'm using JUnit 5 with Spring Boot Security. Using standaloneSetup below snippet is working fine to verify MVC view.
#DisplayName("Test WelcomeController")
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = WelcomeController.class)
public class WelcomeControllerTest {
#InjectMocks
private WelcomeController welcomeController;
#Autowired
protected MockMvc mockMvc;
#MockBean
private RequestContext requestContext;
#BeforeEach
public void setup() {
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
this.mockMvc = MockMvcBuilders.standaloneSetup(welcomeController)
.setCustomArgumentResolvers(new AuthenticationPrincipalArgumentResolver()).build();
}
#Test
public void shouldNavigateToLandingPage() throws Exception {
this.mockMvc.perform(get(AppConstant.SLASH)).andDo(print()).andExpect(status().isOk())
.andExpect(view().name("anonymous/landing/welcome"))
.andExpect(forwardedUrl("anonymous/landing/welcome"))
.andExpect(model().attribute("reset", instanceOf(Reset.class)))
.andExpect(model().attribute("login", instanceOf(Login.class)))
.andExpect(model().attribute("signup", instanceOf(Signup.class)))
.andExpect(model().attribute("subscribe", instanceOf(Subscribe.class)))
.andExpect(header().string("Content-Language", "en"));
}
}
Getting the result as:
But if I use it with WebApplicationContext, I'm getting just 200 OK response and not able to see the ModelAndView, Forwarded URL, View name or anything. Also, the filters are not working.
#DisplayName("Test WelcomeController")
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {WebAppConfig.class, FilterChainProxy.class, SecurityConfig.class, MessageSourceConfig.class})
#WebMvcTest(controllers = WelcomeController.class)
#AutoConfigureMockMvc
#WebAppConfiguration
public class WelcomeControllerTest {
#Autowired
private WebApplicationContext context;
#Autowired
private FilterChainProxy springSecurityFilterChain;
#MockBean
private AuthoritiesLoggingAfterFilter authoritiesLoggingAfterFilter;
#MockBean
private JwtTokenGeneratorFilter jwtTokenGeneratorFilter;
#MockBean
private JwtTokenValidatorFilter jwtTokenValidatorFilter;
#MockBean
private PrivilegeValidatorFilter privilegeValidatorFilter;
#MockBean
private RequestValidationBeforeFilter requestValidationBeforeFilter;
#MockBean
private SameSiteFilter sameSiteFilter;
#MockBean
private AuthenticationService authenticationService;
#MockBean
private CookieSecurityContextRepository cookieSecurityContextRepository;
#MockBean
private RequestContext requestContext;
#Autowired
private WebApplicationContext webApplicationContext;
#InjectMocks
private WelcomeController welcomeController;
#Autowired
protected MockMvc mockMvc;
#BeforeEach
public void setup() {
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
// setting mockMvc with custom securityContext
this.mockMvc = MockMvcBuilders
.webAppContextSetup(this.webApplicationContext)
.apply(springSecurity(springSecurityFilterChain))
.addFilter(springSecurityFilterChain)
.apply(springSecurity(springSecurityFilterChain))
.addFilters(authoritiesLoggingAfterFilter,jwtTokenGeneratorFilter,jwtTokenValidatorFilter,privilegeValidatorFilter,requestValidationBeforeFilter,sameSiteFilter)
.build();
}
#Test
#WithAnonymousUser
public void shouldReturnAllUsersForUnauthenticatedUsers() throws Exception {
this.mockMvc.perform(get(AppConstant.SLASH)).andDo(print())
.andExpect(status().isOk());
}
}
I'm getting response like below:

Related

Spring boot test, #WithMockUser produce NPE in Controller method

i have an issue with my controller test
So my base test class configured like so
#SpringBootTest
#WithMockUser(username = "test_user",authorities = "Configured_allacess_authority")
public abstract class BaseControllerTest extends DatabaseIT {
protected static long counter;
protected MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
protected ObjectMapper objectMapper;
protected MockRestServiceServer restServiceServer;
#Autowired
RestTemplate restTemplate;
#BeforeEach
protected void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
restServiceServer = MockRestServiceServer.createServer(restTemplate);
}
and test code
mockMvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().json(s));
When i debugging my controller method which is
public ResponseEntity<?> getSomething (
#Parameter(description = "ID") final String id,
#ApiIgnore #AuthenticationPrincipal Authentication user){
////
}
user value is null, though when i invoke SecurityContextHolder.getContext().getAuthentication() here in controller i'm getting Authentication object that's refers to one i mocked in
#WithMockUser(username = "test_user",authorities = "Configured_allacess_authority")
So recently i've updated my project to Java 17 , that also forced me to update spring boot to
version: '2.5.5'
and spring cloud to
"org.springframework.cloud:spring-cloud-dependencies:2020.0.5"

How to inject MockMvc with multiple objects in Spring Boot REST Junit test case

I have a SpringBoot REST API connecting to Oracle DB.
My controller calls BusinessImpl layer and in-turn BusinessImpl calls multiple DAO layers (Controller -> BusinessImpl -> DAO1, DAO2, DAO3)
The below test case works perfectly
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration()
#TestPropertySource("classpath:dev-manifest.yml")
#ContextConfiguration(classes = Application.class)
#ConfigurationProperties(prefix = "env")
#SpringBootTest
public class MyTest
{
private static final String REQUEST_URI = "/v1/registration/accounts/links";
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setup()
{
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testSave()
{
String testInput = "some json input";
mockMvc.perform(post(REQUEST_URI).content(testInput).contentType(contentType))
.andExpect(status().isOk());
}
But I do not want to hit real database during junit test case. So I wrote a mock.
Working Code
#Mock
private SecurityAuditDAO securityAuditDAO;
#InjectMocks
private RegistrationBusinessImpl registrationBusinessImpl;
#Test
public void testSave()
{
when(securityAuditDAO.getState(Mockito.any())).thenReturn("somestring");
SomeRequest someRequest = new SomeRequest();
someRequest.setStatus("SUCCESS");
SomeResponse status = registrationBusinessImpl.createUser(SomeRequest, "127.0.0.1");
}
The above code worked perfectly. In businessImpl class securityAuditDAO.getState returned "somestring". But when I introduced mockMvc.perform it stopped working.
Not Working
#Test
public void testSave()
{
when(securityAuditDAO.getState(Mockito.any())).thenReturn("somestring");
String testInput = "some json input";
mockMvc.perform(post(REQUEST_URI).content(testInput).contentType(contentType))
.andExpect(status().isOk());
}
The above code was still hitting the database. So I realized that I should inject mockMvc with securityAuditDAO so I added the following line
this.mockMvc = MockMvcBuilders.standaloneSetup(securityAuditDAO).build();
Code
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Mock
private SecurityAuditDAO securityAuditDAO;
#InjectMocks
private RegistrationBusinessImpl registrationBusinessImpl;
#InjectMocks
RegistrationApiController registrationApiController;
#Before
public void setup()
{
MockitoAnnotations.initMocks(this);
//this.mockMvc = webAppContextSetup(webApplicationContext).build();
this.mockMvc = MockMvcBuilders.standaloneSetup(securityAuditDAO).build();
//this.mockMvc = MockMvcBuilders.standaloneSetup(registrationApiController).build();
//ReflectionTestUtils.setField(mockMvc, "securityAuditDAO", securityAuditDAO);
}
I tried injecting securityAuditDAO. But if I do that, my other autowired instances in BusinessImpl were null.
How to inject securityAuditDAO without affecting others or how to inject both webApplicationContext and securityAuditDAO.
Also tried ReflectionTestUtils.setField but it didn't work as expected.

Spring test invalid configuration

I have a simple REST controller that I'm trying to test, the test looks like this:
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class ModelControllerTests {
private MediaType contentType = MediaType.APPLICATION_JSON_UTF8;
private HttpMessageConverter jacksonConverter;
private MockMvc mockMvc;
#Mock
private ModelService service;
#InjectMocks
private ModelController controller;
#Autowired
private WebApplicationContext context;
#Autowired
void setConverters(HttpMessageConverter<?>[] converters) {
jacksonConverter = Arrays.stream(converters)
.filter(hmc -> hmc instanceof MappingJackson2HttpMessageConverter)
.findAny()
.orElse(null);
assertNotNull(jacksonConverter);
}
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = standaloneSetup(controller)
.build();
}
#Test
public void postModel_returnsModel() throws Exception {
when(service.doSomething(any())).thenReturn(new Model("cde", null));
mockMvc.perform(post("/model")
.content(json(new Model("abc", null)))
.contentType(contentType))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json("{\"notEmpty\":\"abc\"}", true));
}
private String json(Object o) throws IOException {
var responseMessage = new MockHttpOutputMessage();
jacksonConverter.write(o, MediaType.APPLICATION_JSON_UTF8, responseMessage);
return responseMessage.getBodyAsString();
}
}
Now I've got a problem with dependencies and configuration, I've got the following line in my application.properties: spring.jackson.default-property-inclusion=non_null, which works fine when using the normal mockMvc (webAppContextSetup), however I wanted to mock ModelService (which is autowired in ModelController.
When using standaloneSetup to create MockMvc instance there seems to be no configuration, fields that are set to null are returned and furthermore it seems that the ModelService annotated with #Mock is not the same as the one in ModelController, therefore when postModel_returnsModel it's using the wrong service.
How can I solve this?

How can I test my SpringBoot RestController using a MockMvc when I rely on a Spring Validator?

In my rest-controller I am validating the input JSON with a custom Spring validator class.
When I now want to write unit test for the controller then I am getting the error that my Validator could not be found...
I am using constructor injecting for the two components in my rest-controller.
#Autowired
public JobController(JobValidator jobValidator, JobService jobService) {
this.jobValidator = jobValidator;
this.jobService = jobService;
}
And here my corresponding Test class.
#RunWith(SpringRunner.class)
#WebMvcTest(JobsController.class)
#AutoConfigureMockMvc
public class MailMonitorJobControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private JobService jobService;
#Test
public void givenJobs_whenGetJobs_thenReturnJsonArray() throws Exception {
Job job = new Job("TEST");
List<Job> allJobs = Arrays.asList(job);
Mockito.when(jobService.getAllJobs()).thenReturn(allJobs);
mockMvc.perform(get("/api/v1/test")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
Appreciate any help, hints or suggestions!
So thanks to #pvpkiran! I had to add the JobValidator also as a Mock!
#RunWith(SpringRunner.class)
#WebMvcTest(JobsController.class)
#AutoConfigureMockMvc
public class MailMonitorJobControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private JobService jobService;
#MockBean
private JobValidator jobValidator;
#Test
public void givenJobs_whenGetJobs_thenReturnJsonArray() throws Exception {
Job job = new Job("TEST");
List<Job> allJobs = Arrays.asList(job);
Mockito.when(jobService.getAllJobs()).thenReturn(allJobs);
mockMvc.perform(get("/api/v1/test")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}

Spring AOP Aspect not working using Mockito

I have an #Aspect that weaves the execution of all my controller action methods. It works fine when I run the system, but not in unit testing. I'm using Mockito and junit in the following way:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("file:**/spring-context.xml")
#WebAppConfiguration
public class UserControllerTest {
private MockMvc mockMvc;
#Mock
private RoleService roleService;
#InjectMocks
private UserController userController;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
...
mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
}
...
}
with some #Test using mockMvc.perform().
And my Aspect are:
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() { }
#Pointcut("execution(* mypackage.controller.*Controller.*(..))")
public void methodPointcut() { }
#Around("controller() && methodPointcut()")
...
First it is necessary to use webAppContextSetup as Jason suggested:
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() throws Exception {
...
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
At this point the aspect should be triggered but Mockito will not inject mocks. This is because Spring AOP uses a proxy object and the mocks are being injected to this proxy object instead of the proxied object. To fix this it is necessary to unwrap the object and use ReflectionUtils instead of #InjectMocks annotation:
private MockMvc mockMvc;
#Mock
private RoleService roleService;
private UserController userController;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
UserController unwrappedController = (UserController) unwrapProxy(userController);
ReflectionTestUtils.setField(unwrappedController, "roleService", roleService);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
...
public static final Object unwrapProxy(Object bean) throws Exception {
/*
* If the given object is a proxy, set the return value as the object
* being proxied, otherwise return the given object.
*/
if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
Advised advised = (Advised) bean;
bean = advised.getTargetSource().getTarget();
}
return bean;
}
At this point any call to when(...).thenReturn(...) should work properly.
It is explained here: http://kim.saabye-pedersen.org/2012/12/mockito-and-spring-proxies.html
You are probably using Spring AOP, in which case the bean has to be a Spring bean for AOP to work, by not autowiring in the controller it is bypassing the Spring AOP mechanism totally.
I think the fix should be to simply inject in the controller
#Autowired
#InjectMocks
private UserController userController;

Resources