Set authentication for Integration test of secured Controller - spring-boot

I cannot set authentication for my integration test of rest controller. Controller's method looks like this:
#RestController
#RequestMapping(BASE_URL)
#RequiredArgsConstructor
public class EventController {
public static final String BASE_URL = "/api/event";
#PostMapping
#PreAuthorize("hasRole('USER')")
public void createEvent() {
System.out.println("I am in controller");
}
}
and here is my test:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class EventControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void create() throws Exception {
this.mockMvc.perform(post(EventController.BASE_URL)
.with(authentication(new UsernamePasswordAuthenticationToken(
new MyPrincipal(100, "denis"),
null,
Collections.singletonList(new SimpleGrantedAuthority("USER"))
)))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"));
}
My test always failed due to status 401 so my mocked authentication doesn't work. Can you tell me how to fix it? Thank you in advice.

The easiest way to test secured requests is to use #WithMockUser(A_ROLE)
so your test could look like
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#SpringBootTest
#AutoConfigureMockMvc
class EventControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
#WithMockUser("USER")
void create() throws Exception {
this.mockMvc.perform(post(EventController.BASE_URL)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"));
}
}
Some remarks:
your test expects a result, so adopt your controller or test
#PostMapping
#PreAuthorize("hasRole('ROLE_USER')")
public ResponseEntity<String> createEvent() {
String result = "I am in controller";
System.out.println(result);
return ResponseEntity.ok().body(result);
}
you are doing/testing a POST so make sure that in your security configuration you do http.csrf().disable()....
or provide a csrf-token in your test
this.mockMvc.perform(post(EventController.BASE_URL)
.with(SecurityMockMvcRequestPostProcessors.csrf()) // provide a csrf-token
....
'

Related

How to test DELETE, PUT, GET single object requests in Spring Boot?

I'm new to Spring Boot and I'm trying to understand concept of testing.
Recently I was trying to implement DELETE request test for my controller, but I ran into some problems, and I have some quiestions:
Firstly, how can I test a Delete, PUT or GET single object request if there isn't any object to delete? Should I initiate some object at each start of the test?
Secondly, what's the point of injecting Service in such tests? We are mocking it, and that's all. I didn't find out by myself any usage of service in testing. Maybe there is a way to solve the first problem using it?
There is my testing class
package com.example.springrestuser;
import com.example.springrestuser.user.controller.UserController;
import com.example.springrestuser.user.entity.User;
import com.example.springrestuser.user.security.Sha256;
import com.example.springrestuser.user.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.time.LocalDate;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#ExtendWith(SpringExtension.class)
#WebMvcTest(UserController.class)
public class UserControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private UserService userService;
#Test
public void canAddUserTest() throws Exception {
User user = User.builder()
.login("user")
.name("User")
.surname("uuser")
.dateOfBirth(LocalDate.of(2000, 12, 4))
.password(Sha256.hash("useruser"))
.email("user#gmail.com")
.build();
mvc.perform(MockMvcRequestBuilders.post("/api/users")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsString(user)))
.andExpect(status().isCreated());
}
#Test
public void canDeleteUserTest() throws Exception {
User user = User.builder()
.login("user")
.name("User")
.surname("uuser")
.dateOfBirth(LocalDate.of(2000, 12, 4))
.password(Sha256.hash("useruser"))
.email("user#gmail.com")
.build();
userService.add(user); //maybe it would work somehow?
mvc.perform(MockMvcRequestBuilders.delete("/api/users/{login}","user"))
.andExpect(status().isAccepted());
}
#Test
public void canPutUserTest() throws Exception {
User user = User.builder()
.login("user")
.name("User")
.surname("uuser")
.dateOfBirth(LocalDate.of(2000, 12, 4))
.password(Sha256.hash("useruser"))
.email("user#gmail.com")
.build();
//sending the post method and then put?
mvc.perform(MockMvcRequestBuilders.post("/api/users")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsString(user)));
user.setName("new_User");
mvc.perform(MockMvcRequestBuilders.put("/api/users/{login}","user"))
.andExpect(status().isAccepted());
}
#Test
public void canGetSingleUserTest() throws Exception {
User user = User.builder()
.login("user")
.name("User")
.surname("uuser")
.dateOfBirth(LocalDate.of(2000, 12, 4))
.password(Sha256.hash("useruser"))
.email("user#gmail.com")
.build();
mvc.perform(MockMvcRequestBuilders.get("/api/users/{login}", "adam"))
.andExpect(status().isOk());
}
}
There is the controller class
package com.example.springrestuser.user.controller;
import com.example.springrestuser.user.dto.CreateUserRequest;
import com.example.springrestuser.user.dto.GetUserResponse;
import com.example.springrestuser.user.dto.GetUsersResponse;
import com.example.springrestuser.user.dto.UpdateUserRequest;
import com.example.springrestuser.user.entity.User;
import com.example.springrestuser.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
#RestController
#RequestMapping("api/users")
public class UserController {
UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping("")
public ResponseEntity<GetUsersResponse> getUsers() {
List<User> all = userService.findAll();
GetUsersResponse getUsersResponse = GetUsersResponse.entityToDtoMapper(all);
return ResponseEntity.ok(getUsersResponse);
}
#GetMapping("{login}")
public ResponseEntity<GetUserResponse> getUser(#PathVariable("login") String login) {
return userService.find(login)
.map(user -> ResponseEntity.ok(GetUserResponse.entityToDtoMapper(user)))
.orElseGet(() -> ResponseEntity.notFound().build());
}
#PostMapping("")
public ResponseEntity<Void> createUser(#RequestBody CreateUserRequest request) {
User user = CreateUserRequest.dtoToEntityMapper(request);
userService.add(user);
return new ResponseEntity<>(HttpStatus.CREATED);
}
#PutMapping("{login}")
public ResponseEntity<Void> updateUser(#RequestBody UpdateUserRequest request,
#PathVariable("login") String login) {
Optional<User> user = userService.find(login);
if (user.isEmpty()) {
return ResponseEntity.notFound().build();
}
UpdateUserRequest.dtoToEntityMapper(request, user.get());
userService.update(user.get());
return ResponseEntity.accepted().build();
}
#DeleteMapping("{login}")
public ResponseEntity<Void> deleteUser(#PathVariable("login") String login) {
Optional<User> user = userService.find(login);
if (user.isEmpty()) {
return ResponseEntity.notFound().build();
}
userService.delete(user.get());
return ResponseEntity.accepted().build();
}
}
Answering your questions:
With #WebMvcTest you are performing a test to your Controller layer without the rest of the application being loaded (usually called a Slice Test). This means that all you are going to test here is the logic in your Controller in addition to the serialization and deserialization of JSON. This means that you are not actually deleting anything because you are only testing your Controller layer. To actually test the deletion of something in your database you would need to do a fully-fledged Integration Test.
Without a mocked UserService bean, your UserController would be instantiated by Spring. What you should do in this #WebMvcTest is to mock UserService according to the logic you want to test in the Controller. As an example, in your #DeleteMapping("{login}") endpoint you would need to do the following tests:
Test #1: mock userService.find(login) to return an empty Optional<User> and then check that the HTTP Status Code is 404.
Test #2: mock userService.find(login) to return a non-empty Optional<User> and then check that the HTTP Status Code is 202.
Something along the following lines:
#ExtendWith(SpringExtension.class)
#WebMvcTest(UserController.class)
public class UserControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private UserService userService;
#Test
public void shouldDeleteUserIfFound() throws Exception {
// Arrange
String login = "user";
User user = User.builder()
.login(login)
.name("User")
.surname("uuser")
.dateOfBirth(LocalDate.of(2000, 12, 4))
.password(Sha256.hash("useruser"))
.email("user#gmail.com")
.build();
doReturn(Optional.of(user)).when(this.userService).find(login);
// Act & Assert
mvc.perform(MockMvcRequestBuilders.delete("/api/users/{login}", login))
.andExpect(status().isAccepted());
}
#Test
public void shouldNotDeleteUserIfNotFound() throws Exception {
// Arrange
String login = "user";
doReturn(Optional.empty()).when(this.userService).find(login);
// Act & Assert
mvc.perform(MockMvcRequestBuilders.delete("/api/users/{login}", login))
.andExpect(status().isNotFound());
}
}

Junit Test for Controller with parameters

Hi I'm trying to write Junit test for a controller but cant seem to find the proper approach can anyone please suggest how could write a junit test case for the following controller:
Contoller Class
public final class Contoller {
private static final Logger logger = LoggerFactory.getLogger(LiabilityContoller.class);
#Autowired
DemoService demoService;
#GetMapping("/report")
public ResponseEntity<String> getCollection(#RequestParam(name="cohort")String cohort){
List<Book> collection=demoService.getRecords(amount);
bookUtils.writeDataIntoCSVFile(collection, amount);
uploadReportsFilesToSftp(amount);
return new ResponseEntity<String>(" Files Are Generated", HttpStatus.OK);
}
Service class:
#Service
public class DemoService{
#Autowired
DemoRepository demoRepository;
public List<Liability> getRecords(String amount) {
List<Book> list=demoRepository.getRecordsByAmount(amount);
return list;
}
}
Repository
public interface DemoRepository extends JpaRepository<Book,Long>{
#Query(value="Select name,amount from Book where amount=:amount",
List<Book>getRecordsByAmount(String amount);
}
Utility:
public static void writeDataIntoReportsCSVFile(final List<Book> collection,final String amount,final String sftpLocalFile) {
try {
FileWriter FileWriter1 = new FileWriter(sftpLocalFile+"demo.csv");
CSVPrinter FilePrinter1 = new CSVPrinter(FileWriter1, CSVFormat.EXCEL);
printReportsHeader(FileWriter1);
for (Liability obj : collection) {
FilePrinter1.printRecord(obj.getName(),obj.getAmount());
}
FilePrinter1.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static void printReportsHeader(CSVPrinter FilePrinter1) {
try {
FilePrinter1.printRecord("NAME","AMOUNT");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
How could I test this controller what could be the best way to do so?
When you want to unit test a Spring application, the high level idea would be to write tests for the repository layer first, then write unit tests for the service, and then finally the controller layer.
Assuming that you have unit tests for DemoService, bookUtils, and uploadReportsFilesToSftp the Test for the controller would be exactly as below.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#ExtendWith(SpringExtension.class)
#WebMvcTest(Controller.class)
class ControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private DemoService jsonFileService;
#Test
void getCollection() throws Exception {
Mockito.doNothing().when(jsonFileService.getRecords(any()));
// similar stubbings here
this.mockMvc.perform(
get("/report")
.param("cohort", "testCohortValue")
).andExpect(
status().isOk()
);
}
}

Null Pointer Exception on MockMvc.perform on #Test class

I am writing to write a unit test for my RestController (POST) and I am getting a NullPointerException on mvc.perform(...) line.
Here's my RestController :
#RestController
#EnableAutoConfiguration
public class MyController {
#Autowired
private Service1 service;
#Autowired
RestTemplate restTemplate;
#RequestMapping(value = "/logError", method = RequestMethod.POST, produces = {MediaType.APPLICATION_JSON_VALUE})
#ResponseBody
public ResponseEntity ErrorHandlor(#RequestBody JSONStructure jsonStructure) throws Exception{
service.getDocument(jsonStructure.getID(), jsonStructure.getLog());
return new ResponseEntity(HttpStatus.OK);
}
}
And here is my test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {MyController.class,
Service1.class, AppConfig.class})
#WebMvcTest(MyController.class)
public class MyControllerTest {
private MockMvc mockMvc;
#MockBean
private RestTemplate restTemplate;
MyController service = new MyController();
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(service).build();
}
#Test
public void testController() throws Exception{
ObjectMapper mapper = new ObjectMapper();
String url = "http://localhost:8080/logError";
JSONStructure structure = new JSONStructure();
structure.setNumber("num");
structure.setID("id");
structure.setLog("log");
String json = mapper.writeValueAsString(structure)
this.mockMvc.perform
(MockMvcRequestBuilders.post("http://localhost:8080/logError")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(MockMvcResultMatchers.status().isCreated())
.andReturn();
}
}
I am getting a NPE on line containing this.mockMvc.perform(...).
Can anyone point out what might the problem be?
You will get it to work in this way (i tested it):
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
#SpringBootTest
public class TestControllerImplTest {
#Mock
private TestBO TestBO; //if for example the controller calls some autowired service
private MockMvc mockMvc;
#InjectMocks //i think this was your main problem, you missed this annotation
TestControllerImpl controller;
#BeforeEach
void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test()
void shouldReturnAllTests() throws Exception {
TestDTO testDTO = new TestDTO();
testDTO.setId(Long.valueOf(1));
List<TestDTO> allTests = new ArrayList(Arrays.asList(testDTO));
when(testBO.getAllTests()).thenReturn(allTests);
mockMvc.perform(get("/api/test/getAllTests")).andExpect(status().isOk()).andDo(print());
}
}
When using #RunWith(SpringRunner.class), use #Autowired MockMvc mockmvc.
When using #RunWith(MockitoJunitRunner.class) or MockitoAnnotations.initMocks(this) then
use this.mockMvc = MockMvcBuilders.standaloneSetup(service).build();
Don't mix both Spring and Mockito runners.

Unable to Mock RestTemplate.exchange

As part of TDD i want to be able to test every portion of my SpringBoot rest application. However i am unable to mock external calls.
Application structure
1. Few rest endpoints which internally call external rest endpoints.
2. All calls to external endpoints are orchestrated through a local http client which utilizes RestTemplate as httpClient.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.MOCK, classes = TestDrivenDevelopmentWithJavaApplication.class)
public class TestDrivenDevelopmentWithJavaApplicationTests {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#MockBean
private RestTemplate client;
#Before
public void setup() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
Structure1Root category = new Structure1Root();
Category cat = new Category();
cat.setCategoryName("Test1");
cat.setDescription("Test");
category.setD(cat);
Mockito.when(client.exchange(
ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
ArgumentMatchers.eq(Structure1Root.class)))
.thenReturn(new ResponseEntity<Structure1Root>(category, HttpStatus.OK));
}
#Test
public void testendpoint1() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/api/endpoint1?token=1").contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(org.hamcrest.Matchers.containsString("Test1")));
}
}
Even though i have setup the mock on client.exchange(RestTemplate.exchange), i see response returned by client.exchange is null and not the response specified in thenReturn
Controller Code
#RestController
#RequestMapping(path = Endpoint.base)
public class Endpoint {
public static final String base = "/api";
#Autowired
MyHttpClient<Structure2Root> client;
#Autowired
MyHttpClient<Structure1Root> Cclient;
#GetMapping(path = "/endpoint1")
public ResponseEntity<Structure2Root> callEndpt1(#RequestParam String token) {
Response<Structure2Root> resp = client
.execute("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json", Structure2Root.class);
return new ResponseEntity<Structure2Root>(resp.getResponse(), HttpStatus.OK);
}
#GetMapping(path = "/endpoint2")
public ResponseEntity<Structure1Root> callEndpt2(#RequestParam String token) {
Response<Structure1Root> resp = Cclient.execute(
"https://services.odata.org/V2/Northwind/Northwind.svc/Categories(1)?$format=json", Structure1Root.class);
return new ResponseEntity<Structure1Root>(resp.getResponse(),HttpStatus.OK);
}
}
And finally, local http client code
#Service
public class MyHttpClient<O> {
#Autowired
RestTemplate client;
public MyHttpClient() {
// TODO Auto-generated constructor stub
}
public Response<O> execute(String url, Class<O> generic) {
ResponseEntity<O> resp = client.exchange(url, HttpMethod.GET, null, generic);
return new Response<O>(resp.getStatusCode(), resp.getBody());
}
}
this client.execute is what i intend to intercept in the first code block
However never seems to work and always returns a null.
The Git Repo
Regards,
Veera
You have used the wrong object while mocking. You should be using Structure2Root rather then Structure1Root
The correct test class is below which is working perfectly fine.
package com.demo.samples.tdd;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.demo.samples.tdd.responses.Product;
import com.demo.samples.tdd.responses.Structure2Root;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.WebApplicationContext;
import com.demo.samples.tdd.responses.Category;
import com.demo.samples.tdd.responses.Structure1Root;
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.MOCK, classes = TestDrivenDevelopmentWithJavaApplication.class)
public class TestDrivenDevelopmentWithJavaApplicationTests {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#MockBean
private RestTemplate client;
#Before
public void setup() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
// Structure1Root category = new Structure1Root();
// Category cat = new Category();
// cat.setCategoryName("Test1");
// cat.setDescription("Test");
// category.setD(cat);
//
// Mockito.when(client.exchange(
// ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
// ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
// ArgumentMatchers.eq(Structure1Root.class)))
// .thenReturn(new ResponseEntity<Structure1Root>(category, HttpStatus.OK));
Structure2Root category2 = new Structure2Root();
Product product = new Product();
product.setProductName("Test1");
product.setUnitPrice("1");
category2.setD(product);
Mockito.when(client.exchange(
ArgumentMatchers.eq("https://services.odata.org/V2/Northwind/Northwind.svc/Products(1)?$format=json"),
ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.eq(null),
ArgumentMatchers.eq(Structure2Root.class)))
.thenReturn(new ResponseEntity<Structure2Root>(category2, HttpStatus.OK));
}
#Test
public void testendpoint1() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/api/endpoint1?token=1").contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(org.hamcrest.Matchers.containsString("Test1")));
}
}

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