Reuse Spring Mockmvc in unit tests - spring

Can Mockmvc be reused within a test suite? I have several test suites so having to initialize the mockmvc between every test and then running all tests is really slow!
I've tried putting mockMvc into the annotation #BeforeClass rather than in #Before but because it is a static method, the WebApplicationContext and FilterChainProxy are autowired so cannot be referenced in a static method as far as I'm aware.
My tests are currently set up like:
#ContextConfiguration(classes = {Application.class})
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public class MyTest {
#Autowired
private static WebApplicationContext wac;
#Autowired
private static FilterChainProxy springSecurityFilter;
private MockMvc mockMvc;
#Before
public void setUp() {
assertNotNull(wac);
assertNotNull(springSecurityFilter);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilters(springSecurityFilter).build();
}
Does anyone know how/if it is possible to reuse Mockmvc? It seems like an expensive operation to set up and tear down for every test.
Any help would be much appreciated, thanks.

How about a #Before method which doesn't overwrite the mockMvc if it is set.
private static MockMvc mockMvc = null;
#Before
public void setUp() {
assertNotNull(wac);
assertNotNull(springSecurityFilter);
if (this.mockMvc == null) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilters(springSecurityFilter).build();
}
}

Related

java.lang.AssertionError: Status Expected :200 Actual :500

I wrote a small code to test my Controller and used mockito in this regard. The code is shown below with correct syntaxes
public class ApiTest {
private MockMvc mockMvc;
#InjectMocks
private PersonController personController;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(personController).build();
}
#Test
public void testPersonController() throws Exception{
//String jsonResult = "{\"name\": \"Müller\", \"lastName\": \"Hans\", \"zipCode\": \"67742\", \"city\": \"Lauterecken\", \"color\": \"blau\", \"id\": 1}";
mockMvc.perform(MockMvcRequestBuilders.
get("/api/v1/person/persons/1").
accept(MediaType.APPLICATION_JSON)).
andDo(print()).
andExpect(status().isOk()).
andExpect(MockMvcResultMatchers.jsonPath("$.id").value("1")).
andExpect(MockMvcResultMatchers.jsonPath("$name").value("Müller"))
.andExpect(MockMvcResultMatchers.jsonPath("$lastName").value("Hans")).
andExpect(MockMvcResultMatchers.jsonPath("$zipCode").value("67742"))
.andExpect(MockMvcResultMatchers.jsonPath("$city").value("Lauterecken"))
.andExpect(MockMvcResultMatchers.jsonPath("$color").value("blau"));
}
}
I am receiving an assertion error as shown below
java.lang.AssertionError: Status
Expected :200
Actual :500
Can anyone help me where I am going wrong?
Looks like you're not fully utilizing what Spring Boot offers to test controllers.
When you're testing with MockMvc:
annotate your class with #WebMvcTest
use #MockBean to mock any collaborators this controller depends on
autowire MockMvc instead of creating it
#WebMvcTest(PersonController.class)
public class ApiTest {
#Autowired
private MockMvc mockMvc;
#MockBean
SomeService someService; // only if controller has dependencies on any components
#Test
void aTest() {
mockMvc.perform(...)
// ...
}

MockMVC always Null

MockMvc is always null! i also tried other annotations like it is described at other questions but nothing works.
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = HelpPageController.class)
public class HelpPageControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void test() throws Exception {
//other code
ResultActions result = mockMvc.perform(MockMvcRequestBuilders.get("/help/manuals"));
//other code
}
You should add #AutoConfigureMockMvc annotation, so the MockMvc can be injected in your test class.

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.

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