How do I change this Spring Test Framework Test to work with a JSON return? - spring

I am trying to change the following Spring Test Framework to test for a the following json return:
{"user":"John ","name":"JohnSmith"}
Here is my test code and I just dont know how to change it to check for JSON and the value in JSON. It will be great if someone can tell me what to change.. thanks
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebMVCConfig.class})
#WebAppConfiguration
public class TestHelloWorldWeb
{
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup()
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getFoo() throws Exception
{
this.mockMvc.perform(get("/ask")
.accept(MediaType.TEXT_HTML))
.andExpect(status().isOk())
.andExpect(view().name("helloworld"))
.andExpect(MockMvcResultMatchers.model().attribute("user", "JohnSmith"))
;
}
}

.andExpect(jsonPath("$.user").value("john"));
with dependency :
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.8.1</version>
</dependency>
and one static import like this
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
adds the static method...

Related

the #Mock and #InjectMocks annotations don't work

rest
#RestController
public class StackOverFlowController {
private StackOverFlowService service;
#Autowired
public StackOverFlowController(
#Qualifier(value = "stackOverFlowServiceImpl")
StackOverFlowService service) {
this.service = service;
}
#RequestMapping("api/stackoverflow")
public List<StackOverFlowDto> getListOfProviders() throws URISyntaxException {
List<StackOverFlowDto> allSites = service.getAllSites();
return allSites;
}
}
service
public interface StackOverFlowService {
List<StackOverFlowDto> getAllSites() throws URISyntaxException;
List<StackOverFlowDto> getAllSitesByTitle(String title) throws URISyntaxException;
}
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${version.mapstruct}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
test
#RunWith(MockitoJUnitRunner.class)
public class StackOverFlowControllerTest {
#Mock
private StackOverFlowService service ;
#InjectMocks
private StackOverFlowController controller;
#Test
public void getListOfProviders() throws URISyntaxException {
List<StackOverFlowDto> allSites = service.getAllSites();
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
verify(service).getAllSites();
}
}
I can create an mock-object this way: :
private StackOverFlowService service = Mockito.mock(StackOverFlowService.class);
update 1
update 2
Wnen I have done this:
#RunWith(SpringRunner.class)
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTest {
#Mock
private StackOverFlowService service;
#InjectMocks
StackOverFlowController controller;
#Test
public void getListOfProviders() throws URISyntaxException {
List<StackOverFlowDto> allSites = service.getAllSites();
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
verify(service).getAllSites();
}
}
I got an error:
PM org.junit.vintage.engine.descriptor.RunnerTestDescriptor warnAboutUnfilterableRunner WARNING: Runner
org.junit.internal.runners.ErrorReportingRunner (used on class
com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest)
does not support filtering and will therefore be run completely.
org.junit.runners.model.InvalidTestClassError: Invalid test class
'com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest':
No runnable methods
where is the method get() from ?
update 3
This the test was performed:
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#MockBean
private StackOverFlowController controller;
#Test
public void getListOfProviders() throws Exception {
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
assertNotNull(listOfProviders);
}
}
But I don't see the point. I don't get any data:
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
But here I should get a collection of elements, but I get 0
Update 4
It is my the understanding:
when(service.getAllSites()).thenReturn(ImmutableList.of());
We test a controller. We don't need below layers. We need to validate that the method
getListOfProviders()
of a contoller is called.
So,
service.getAllSites()
here we point Mockito-object
#MockBean
private StackOverFlowService service;
This method should return some sort of collection.
thenReturn(ImmutableList.of())
it should return empty collection.
Why ?
I don't understand this.
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
What is it for?
Update 5
#Test
public void getListOfProviders() throws Exception {
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
assertNotNull(listOfProviders);
verify(service).getAllSites();
}
verify(service).getAllSites();
This should validate that the method getAllSites() from interface StackOverFlowService (from its implemetation), will call...
But Again I got errors:
Wanted but not invoked:
com.spring.mongo.service.read.stackoverflow.StackOverFlowService#0
bean.getAllSites();
-> at com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest.getListOfProviders(StackOverFlowControllerTest.java:38)
Actually, there were zero interactions with this mock.
Wanted but not invoked:
com.spring.mongo.service.read.stackoverflow.StackOverFlowService#0
bean.getAllSites();
-> at com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest.getListOfProviders(StackOverFlowControllerTest.java:38)
Actually, there were zero interactions with this mock.
Why ?
Update 6
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoApplication.class})
#WebMvcTest(StackOverFlowController.class)
class StackOverFlowControllerTestUnit {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#MockBean
private StackOverFlowController controller;
#Test
void getListOfProviders() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders
.get("/api/stackoverflow")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder)
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.id").exists())
.andDo(print());
}
}
Here beans is created, but i don't result into json...
.andExpect(MockMvcResultMatchers.jsonPath("$.sites").exists())
.andExpect(MockMvcResultMatchers.jsonPath("$.sites[*].id").isNotEmpty());
Update_7
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoApplication.class})
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTestFromForum {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#InjectMocks
private StackOverFlowController controller;
#Test
public void getListOfProviders() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders
.get("/api/stackoverflow")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder)
.andDo(print())
.andExpect(status().isOk());
}
}
In this version of the code, all beans are initialized without errors.
#InjectMocks - It is work also.
Normal integration testing offered by Spring simply creates objects
as if the application were running in production. I still don't
understand what Mock-objects are for.
Here any ideas is finished.
I don't understand why it dosen't work.
Сan someone explain the idea of #Mock and #InjectMocks annotations working in relation to this example ?
Does anyone have any ideas?
If you want the controller test to be the same like any other unit test case class then use spring for running tests using annotation #RunWith(SpringRunner.class) .Since you are writing the unit test case for the controller , use the test method like below.You just need to mock the service call and call the controller method or more appropriately hit it via mock mvc :
#Test
public void getListOfProviders() throws URISyntaxException {
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
assertNotNull(listOfProviders);
}
But if you want it to be the right way then :
#WebMvcTest
public class StackOverFlowControllerTest {
#Mock
private StackOverFlowService service ;
#Autowired
private MockMvc mockMvc;
#Test
public void getListOfProviders() throws URISyntaxException {
when(service.getAllSites()).thenReturn(ImmutableList.of());
this.mockMvc.perform(get("api/stackoverflow")).andDo(print()).andExpect(status().isOk());
}
}
If you are having issues ith #WebMvcTest it means springboot is having trouble setting up the test env because of project structure or annotation on main application class that might be causing issues for test setup so in that case try setting up an entire application context like in an integration test (a bit tedious and unnecessary ) by using the #SpringBootTest annotation instead.
Official doc link.
Try with #RunWith(SpringRunner.class) instead of #RunWith(MockitoJUnitRunner.class), it should work. SpringRunner will help with Dependency Injection of the mocks.
Edit:
Try with the code below:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoApplication.class})
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTestUnit {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#Test
void getListOfProviders() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders
.get("/api/stackoverflow")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder)
.andDo(print())
.andExpect(status().isOk());
// .andExpect(MockMvcResultMatchers.jsonPath("$.id").exists())
// .andDo(print());
}
}
Try going step by step. If the above works then maybe you can go for the json path verification

Why do I keep getting 404's when using #AutoConfigureMockMvc in SpringBoot test?

I'm trying to write an integration test using springrunner. I'm running it as a springboottest and using autoconfiguremockmvc. However, i keep getting 404s. It seems my controllers/endpoints aren't being loaded by the autoconfiguremockmvc. Does anyone know a solution of how to wire this up so that it will pick up my controllers?
I did add a basic controller into my test class and i was able to hit it successfully but I have been unable so far to hit the actual controller I want to use in my integration test.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { TestConfig.class })
#TestPropertySource(locations = "classpath:application-test.properties")
#AutoConfigureMockMvc
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void test() throws Exception {
MockHttpServletRequestBuilder request =
MockMvcRequestBuilders.post(URL).contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.content(json);
final ResponseEntity<String> response =
new ResponseEntity<>("works", HttpStatus.OK);
final ResultActions result = this.mockMvc.perform(request).andDo(print());
result.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
}
}
#TestConfiguration
#EnableAutoConfiguration
public class TestConfig {
}
You could use
#SpringBootTest(webEnvironment =
SpringBootTest.WebEnvironment.RANDOM_PORT)
and RestAssured library in combination to test any endpoint/api like below
// include dependency in pom
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.0.5</version>
<scope>test</scope>
</dependency>
//Test Class
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntTest{
//inject test local port
#LocalServerPort
private int port;
#Before
public void setUp() {
//assign port for reset assured
RestAssured.port = port;
}
#Test
public void test() {
given()
.get("/api")
.then()
.assertThat()
.statusCode(HttpStatus.OK.value())
.contentType(ContentType.JSON)
.body("name", is("test"));
}
}

spring-boot Test JpaRepository is null

I am trying to execute a JUnit test restful with spring boot application but it is wrong. But if I use a browser it's OK!
Help.
The Repository class:
package com.zhx.help.dao;
import com.zhx.help.model.Girl;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import javax.transaction.Transactional;
import java.util.List;
#Repository
#Transactional
public interface GirlRepository extends JpaRepository<Girl,Integer> {
List<Girl> findByAge(Integer age);
}
Controller class
package com.zhx.help.controller;
import com.zhx.help.dao.GirlRepository;
import com.zhx.help.model.Girl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
public class GirlController {
#Autowired
private GirlRepository girlRepository;
/**查询所有女生列表*/
#GetMapping(value = "/girls")
public List<Girl> girlList(){
return girlRepository.findAll();
}
The JUnit:
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class GirlControllerTest extends MockMvcResultHandlers {
private static Logger log = LoggerFactory.getLogger(GirlControllerTest.class);
//模拟对象
private MockMvc mvc;
#MockBean
private GirlRepository girlRepository;
#Before
public void setUp() {
mvc = MockMvcBuilders.standaloneSetup(new GirlController()).build();
}
#Test
public void girlList() throws Exception {
RequestBuilder request = MockMvcRequestBuilders.get("/girls");
mvc.perform(request).andExpect(status().isOk());
}
The code
https://github.com/longfeizheng/springboot-oracle.git
With your current approach you are basically rendering #SpringBootTest useless. You are loading everything and the first thing you do in the setup is basically discard all the results.
Either use the started context by autowiring the MockMvc or create a simple unit test using Mockito.
Using a preconfigured MockMVC
By simply adding #Autowired on your MockMvc field you should be able to get the prepared instance and you can simply remove your setup method.
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class GirlControllerTest {
private static Logger log = LoggerFactory.getLogger(GirlControllerTest.class);
//模拟对象
#Autowired
private MockMvc mvc;
#MockBean
private GirlRepository girlRepository;
#Test
public void girlList() throws Exception {
RequestBuilder request = MockMvcRequestBuilders.get("/girls");
mvc.perform(request).andExpect(status().isOk());
}
Create a simple Unit test instead of Integration test
Another option is to not load the whole context and simply directly use Mockito and the standaloneSetup you have now.
#RunWith(MockitoJUnitRunner.class)
public class GirlControllerTest {
private static Logger log = LoggerFactory.getLogger(GirlControllerTest.class);
//模拟对象
private MockMvc mvc;
#Mock
private GirlRepository girlRepository;
#InjectMocks
private GirlController girlController;
#Before
public void setUp() {
mvc = MockMvcBuilders.standaloneSetup(girlController).build();
}
#Test
public void girlList() throws Exception {
RequestBuilder request = MockMvcRequestBuilders.get("/girls");
mvc.perform(request).andExpect(status().isOk());
}
Either way will work and which you need/want depends on your needs and what you want (simply test the controller or make it a huge integration test).
Note: You are extending MockMvcResultHandlers don't extend that class just use static imports instead.
You should not create a controller object using new ..
You can do something like below:
#InjectMocks
private GirlController girlController;
mvc = MockMvcBuilders.standaloneSetup(girlController).build();
You can avoid mocking a repository class. Instead create a service class and mock it and access Repository class inside the service class

Mocked Spring #Service that has #Retryable annotations on methods fails with UnfinishedVerificationException

I'm using Spring Boot 1.4.0.RELEASE with spring-boot-starter-batch, spring-boot-starter-aop and spring-retry
I have a Spring Integration test that has a #Service which is mocked at runtime. I've noticed that if the #Service class contains any #Retryable annotations on its methods, then it appears to interfere with Mockito.verify(), I get a UnfinishedVerificationException. I presume this must be something to do with spring-aop? If I comment out all #Retryable annotations in the #Service then verify works ok again.
I have created a github project that demonstrates this issue.
It fails in sample.batch.MockBatchTestWithRetryVerificationFailures.batchTest() at validateMockitoUsage();
With something like:
12:05:36.554 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext#5ec0a365 testClass = MockBatchTestWithRetryVerificationFailures, testInstance = sample.batch.MockBatchTestWithRetryVerificationFailures#5abca1e0, testMethod = batchTest#MockBatchTestWithRetryVerificationFailures, testException = org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at sample.batch.service.MyRetryService$$FastClassBySpringCGLIB$$7573ce2a.invoke(<generated>)
Example of correct verification:
verify(mock).doSomething()
However I have another class (sample.batch.MockBatchTestWithNoRetryWorking.batchTest()) with a mocked #Service that doesn't have any #Retryable annotation and verify works fine.
What am I doing wrong?
In my pom.xml I have the following:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
...
Then all the related Java Classes
#SpringBootApplication
#EnableBatchProcessing
#Configuration
#EnableRetry
public class SampleBatchApplication {
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Autowired
private MyRetryService myRetryService;
#Autowired
private MyServiceNoRetry myServiceNoRetry;
#Bean
protected Tasklet tasklet() {
return new Tasklet() {
#Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext context) {
myServiceNoRetry.process();
myRetryService.process();
return RepeatStatus.FINISHED;
}
};
}
#Bean
public Job job() throws Exception {
return this.jobs.get("job").start(step1()).build();
}
#Bean
protected Step step1() throws Exception {
return this.steps.get("step1").tasklet(tasklet()).build();
}
public static void main(String[] args) throws Exception {
// System.exit is common for Batch applications since the exit code can be used to
// drive a workflow
System.exit(SpringApplication
.exit(SpringApplication.run(SampleBatchApplication.class, args)));
}
#Bean
ResourcelessTransactionManager transactionManager() {
return new ResourcelessTransactionManager();
}
#Bean
public JobRepository getJobRepo() throws Exception {
return new MapJobRepositoryFactoryBean(transactionManager()).getObject();
}
}
#Service
public class MyRetryService {
public static final Logger LOG = LoggerFactory.getLogger(MyRetryService.class);
#Retryable(maxAttempts = 5, include = RuntimeException.class, backoff = #Backoff(delay = 100, multiplier = 2))
public boolean process() {
double random = Math.random();
LOG.info("Running process, random value {}", random);
if (random > 0.2d) {
throw new RuntimeException("Random fail time!");
}
return true;
}
}
#Service
public class MyServiceNoRetry {
public static final Logger LOG = LoggerFactory.getLogger(MyServiceNoRetry.class);
public boolean process() {
LOG.info("Running process that doesn't do retry");
return true;
}
}
#ActiveProfiles("Test")
#ContextConfiguration(classes = {SampleBatchApplication.class, MockBatchTestWithNoRetryWorking.MockedRetryService.class}, loader = AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class MockBatchTestWithNoRetryWorking {
#Autowired
MyServiceNoRetry service;
#Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
#Bean
#Primary
public MyServiceNoRetry myService() {
return mock(MyServiceNoRetry.class);
}
}
}
#ActiveProfiles("Test")
#ContextConfiguration(classes = { SampleBatchApplication.class,
MockBatchTestWithRetryVerificationFailures.MockedRetryService.class },
loader = AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class MockBatchTestWithRetryVerificationFailures {
#Autowired
MyRetryService service;
#Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
#Bean
#Primary
public MyRetryService myRetryService() {
return mock(MyRetryService.class);
}
}
}
EDIT: Updated question and code based on a sample project I put together to show the problem.
So after looking at a similar github issue for spring-boot
I found that there is an extra proxy getting in the way. I found a nasty hack by unwrapping the aop class by hand, makes verification work, ie:
#Test
public void batchTest() throws Exception {
service.process();
if (service instanceof Advised) {
service = (MyRetryService) ((Advised) service).getTargetSource().getTarget();
}
verify(service).process();
validateMockitoUsage();
}
Hopefully, this can be fixed similar to the above github issue. I'll raise an issue and see how far I get.
EDIT: Raised the github issue
After I've seen #Joel Pearsons answer, and especially the linked GitHub issue, I worked around this by temporarily using a static helper method that unwraps and verifies:
public static <T> T unwrapAndVerify(T mock, VerificationMode mode) {
return ((T) Mockito.verify(AopTestUtils.getTargetObject(mock), mode));
}
With this method the only difference in the test cases is the verification call. There is no overhead other than this:
unwrapAndVerify(service, times(2)).process();
instead of
verify(service, times(2)).process();
Actually, it was even possible to name the helper method like the actual Mockito method, so that you only need to replace the import, but I didn't like the subsequent confusion.
However, unwrapping shouldn't be required if #MockBean is used instead of mock() to create the mocked bean. Spring Boot 1.4 supports this annotation.

how to use jmockit with spring's mockmvc to test controller

I want to use mockmvc to test controller which is recommended by Spring. But, I also have to use jmockit to mock the dependences.
The problem is that jmockit can't do well with mockmvc, whether the standaloneSetup() or the webAppContextSetup().
Another mocking tool named Mockito is well done with this problem, but it has a lot limits in mocking dependencies.
So, anybody has the experience or idea, please tell me. Thank you very much.
The example code is as following:
The first is the Mockito with Spring's MockMvc to unit test controller. This runs well.
public class TestControllerTest {
#InjectMocks
private LoginController loginController;
#Mock
private LoginService loginService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(loginController).build();
}
#Test
public void testLogin() throws Exception {
when(loginService.login()).thenReturn(false);
this.mockMvc.perform(get("/login"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(view().name("goodbyeworld"))
.andReturn();
}
}
Secondly, the jmockit is as following. Unfortunately, the loginController is null at the setup method. And, if i just invoke the loginController.xxx() in the #Tested method is fine. I think this shows that loginController is instantiated before #Tested method but after #Before method.
public class TestControllerTest2 {
#Tested
private LoginController loginController;
#Injectable
private LoginService loginService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.standaloneSetup(loginController).build();
}
#Test
public void testLogin() throws Exception {
new Expectations() {{
loginService.login(); result = false;
}};
this.mockMvc.perform(get("/login"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(view().name("goodbyeworld"))
.andReturn();
}
}
So, how can this problem be solved? jmockit's handful init method? any possible?
Differently from Mockito's #InjectMocks, JMockit's #Tested fields get created only after the execution of any #Before methods. This happens because of the support for mock parameters in test methods, which doesn't exist in Mockito. Arguably, tested fields should be set early, together with mock fields, so this may change in a future version of JMockit.
Anyway, solutions for the problem as it stands today are:
Do not use #Tested; instead, instantiate and inject the object under test manually in the #Before method.
Use #Tested, but avoid #Before methods which depend on tested fields. In the example test, the MockMvc object could be created in each test method by calling a MockMvc mockMvc() { return MockMvcBuilders... } method.
I have faced similar problem recently, and I have found a little bit of graceful solution:
#Tested(availableDuringSetup=true)
NotificationController notificationController;
#Injectable
NotificationService notificationService;
private MockMvc mockMvc;
#Before
public void init() {
this.mockMvc = MockMvcBuilders.standaloneSetup(notificationController).build();
}
boolean availableDuringSetup attribute for #Tested annotation is the solution :)
Hope that helps,
The problem is jmockit can't do well with mockmvc
I find that JMockit and Spring's MockMvc do play together well enough. I have successfully used the webAppContextSetup in my case. Here is an example which may not compile even, but could be a useful guide to get you started..
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import mockit.*;
import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import some.package.Account;
import some.package.Collaborator;
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#WebAppConfiguration
#ContextConfiguration(locations = { "classpath:/context/example1.xml", "classpath:/context/example2.xml" })
public class AccountControllerIntegrationTest {
private static final String PATH_TO_ACCOUNT = "/accounts/some_account";
private String exampleAccountJson = "{\"account\":\"Sample\",\"active\":true}";
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Mocked
private Account mockAccount;
#Mocked
private Collaborator mockCollaborator;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
public void should_delete_account() throws Exception {
new Expectations() {{
mockAccount.getSomethingWhichReallyShouldNotBeExposed(); result = mockCollaborator;
mockCollaborator.getSomething(); result = "whatever";
}};
mockMvc.perform(delete(PATH_TO_ACCOUNT)).andExpect(status().isOk());
}
#Test
public void should_add_account() throws Exception {
new NonStrictExpectations() {{
mockAccount.getSomethingWhichReallyShouldNotBeExposed(); result = mockCollaborator;
mockCollaborator.getSomething(); result = "whatever";
}};
mockMvc.perform(put(PATH_TO_ACCOUNT).contentType(MediaType.APPLICATION_JSON).content(exampleAccountJson)).andExpect(status().isOk());
}
}
Hope it can help you--good luck!

Resources