Controller
#PostMapping("/event/confirmation")
public Mono<CallbackResponse> eventConfirmation(#RequestParam("eventId") String eventId,
#RequestBody ExecutionSummary execSummary, #RequestHeader("customerCode") String customerCode)
throws ResourceNotFoundException {
return eventService
.updateEvent(eventId, execSummary, serviceAuthClient.getDefaultJwtTokenObserver().getJwtAccessToken())
.flatMap(event -> {
return Mono.just(new CallbackResponse(true));
});
}
Test Class
#RunWith(SpringRunner.class)
#WebAppConfiguration
#AutoConfigureMockMvc
#SpringBootTest(properties = "spring.main.banner-mode=off")
public class EventStreamControllerTest {
#Mock
private ServiceAuthClient serviceAuthClient;
#Mock
private EventService eventService;
#InjectMocks
private EventStreamController eventStreamController;
#Before
public void setUp() {
Mockito.reset(eventService);
mvc = MockMvcBuilders.standaloneSetup(eventStreamController)
.setControllerAdvice(new ControllerExceptionHandler()).build();
}
#Test
public void testEventConformation() throws Exception {
ExecutionSummary executionSummary = new ExecutionSummary();
executionSummary.setErrorMessage("Test");
Mono<Event> event = Mono.just(new Event());
Mockito.when(eventService.updateEvent(Mockito.anyString(),
Mockito.any(), Mockito.anyString()))
.thenReturn(event);
RequestBuilder requestBuilder =
MockMvcRequestBuilders.post("/event/confirmation")
.contentType(MediaType.APPLICATION_JSON).header("customerCode", "456")
.param("eventId", "123")
.content(objectMapper.writeValueAsString(executionSummary));
MvcResult result = mvc.perform(requestBuilder).andReturn();
assertEquals(HttpStatus.OK.value(),result.getResponse().getStatus());
}
}
When i run the test it's actually calling original service it's not using mock service.
Can someone help me how to write a test for above controller method.
When i print the mock service invocations it's printing below output.
[Mockito] Unused stubbings of: eventService
1. eventService.updateEvent("", null, "");
- stubbed -> at com.endpoint.rest.controller.EventStreamControllerTest.testEventConformation(EventStreamControllerTest.java:158)
In you test class, you should define your eventService as a MockBean. Then it will be a mock service.
#MockBean
private EventService eventService;
Related
I'm trying test a RestAPI in Java, but I can't mock my Service class
When start test, it gives me an error: java.lang.NullPointerException: Cannot invoke "com.tcc.tccbackend.services.PacienteService.findById(java.lang.Integer)" because "this.service" is null.
Code:
#WebMvcTest(PacienteController.class)
public class PacienteTest extends BaseTest {
#Mock
private PacienteService service;
#Autowired
private MockMvc mockMvc;
#BeforeEach
public void setup() {
RestAssuredMockMvc.mockMvc(mockMvc);
}
#Test
#DisplayName("Retorna sucesso quando busca um paciente ")
public void t4() {
Mockito.when(service.findById(9999))
.thenReturn(new Paciente(9999, "Gilberson", "gilber#gmail.com", "68211836104", "(67) 99625-5371", new Date(), List.of()));
RestAssuredMockMvc
.given()
.header("Authorization", getJWT())
.accept(ContentType.JSON)
.when()
.get("/pacientes/9999")
.then().statusCode(200);
}
BaseTest.class:
public class BaseTest {
public BaseTest(){
baseURI = "http://localhost";
port = 8080;
basePath = "/api";
}
public static String getJWT() {
return given()
.body("{\n" + "\t\"email\": \"paula#gmail.com\",\n" + "\t\"senha\": \"senha\"\n" + "}")
.contentType(ContentType.JSON)
.when()
.post("/user/login")
.then()
.extract()
.path("token");
}
}
Versions:
Java 17
RestAssured 5.3.0
SpringBoot 2.7.0
SpringBoot Starter Test 2.7.0
Junit 4.13.2
Your mixing up unit test and integration test config.
Annotations for unit tests look like this:
#ExtendWith(MockitoExtension.class)
class MyTest {
#Mock
private ServiceA myMock;
#Spy
private ServiceB mySpy;
#InjectMocks
private ServiceC myClassUnderTest;
#Test
void receive() {
// test logic
}
}
Annotations for integration tests look like this:
#ActiveProfiles("test")
#SpringBootTest
class MyTest {
#MockBean
private ServiceA myMock;
#SpyBean
private ServiceB mySpy;
#Autowired
private ServiceC myClassUnderTest;
#Test
void receive() {
// test logic
}
}
I don't know about your test config because you didn't post it here, but I suppose you should use #MockBean instead of #Mock for PacienteService service.
I need to write a unit test for a rest controller endpoint that calls a service which calls RestTemplate exchange()
#RestController
public class MyController {
#Autowired
Myservice myservice;
#GetMapping("todo")
public ResponseEntity<String> getTodo() {
String todoJson = myservice.getTodo();
return ResponseEntity.ok(todoJson);
}
}
Here's my service class
#Service
public class Myservice {
public String getTodo() {
ResponseEntity<Todo> response = restTemplate.exchange("https://jsonplaceholder.typicode.com/todos/1", HttpMethod.GET, null, Todo.class);
Todo todo = response.getBody();
return objectMapper.writeValueAsString(todo);
}
}
And test case
#ExtendWith(SpringExtension.class)
class MyControllerTestJunit5 {
private MockMvc mockMvc;
#Mock
private RestTemplate restTemplate;
#InjectMocks
MyController myController;
#Mock
Myservice myservice;
#BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
#Test
public void mockResttemplate() throws Exception {
Todo todosample = new Todo(5,6,"myfield", true);
ResponseEntity<Todo> responseEntity = ResponseEntity.ok(todosample);
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.eq(Todo.class)))
.thenReturn(responseEntity);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/todo"))
.andExpect(status().isOk())
.andReturn();
String myresult = result.getResponse().getContentAsString();
System.out.println("response: " + myresult);
}
printing the response at the end shows that the response is empty. How can I get this to work?
I assume that you expect to get the JSON representation of todosample object. The problem in the test is that you are mocking Myservice by annotating it with #Mock. That mocked service is used by MyController because it's annotated with #InjectMocks. As a result, when you make a call to the /todo endpoint the controller calls myservice.getTodo() which returns null as the Myservice mock is not configured to return anything else. After that, the null value gets propagated to the ResponseEntity body which materializes to the empty OK response in your test.
I believe a better approach for this kind of test will be to just mock Myservice and configure it properly. The first part is done already as I mentioned. The configuration is easy. You should just replace restTemplate.exchange call configuration with the myservice.getTodo() one. Something like this should work:
#Test
public void mockResttemplate() throws Exception {
String todosampleJson = "{}"; // just empty json
when(myservice.getTodo()).thenReturn(todosampleJson);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/todo"))
.andExpect(status().isOk())
.andReturn();
String myresult = result.getResponse().getContentAsString();
System.out.println("response: " + myresult); // will print "{}" now
}
And then you can have a separate test for Myservice where you have to mock restTemplate.exchange call.
Of course technically, you can still get away with mocking the restTemplate in your MyController test but then you have to make sure you instantiate the real Myservice instead of the mocked version of it. However, in this case you would expose the implementation details of Myservice to the MyController class, which kinda contradicts the MVC pattern.
I have a SpringBoot application with this method in the controller to create an user in the database. The controller is working fine in Postman.
#RestController
#RequestMapping("/v1")
public class UserController {
#PostMapping(value = "/user/{id}")
public void createUser(#PathVariable Integer id, #Valid #RequestBody User request,
BindingResult bindingResult) throws Exception {
if (bindingResult.hasErrors()) {
throw new RequestValidationException(VALIDATION_ERRORS, bindingResult.getFieldErrors());
}
userService.createUser(id, request), HttpStatus.CREATED);
}
Now I have a junit test case to test this method and I am getting a 404
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApp.class)
public class UserTest {
private MockMvc mockMvc;
final String CREATE_USER_URL = "/v1/user/" + "10";
private final MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post(CREATE_USER_URL)
// doesn't work either if I put "/v1/user/10" or post("/v1/user/{id}", 10) here
.content(TestUtils.toJson(request, false))
.contentType(contentType))
.andDo(print())
.andExpect(status().isCreated())
.andReturn();
}
But in the log, I was able to see the correct url:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /v1/user/10
Parameters = {}
Can someone please let me know why I am getting a 404 NOT Found? Thanks.
From docs you need #AutoConfigureMockMvc on class and #Autowire MockMvc
Another useful approach is to not start the server at all, but test only the layer below that, where Spring handles the incoming HTTP request and hands it off to your controller. That way, almost the full stack is used, and your code will be called exactly the same way as if it was processing a real HTTP request, but without the cost of starting the server. To do that we will use Spring’s MockMvc, and we can ask for that to be injected for us by using the #AutoConfigureMockMvc annotation on the test case:
Code :
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class UserTest {
#Autowire
private MockMvc mockMvc;
final String CREATE_USER_URL = "/v1/user/" + "10";
private final MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Test
public void testCreateUser() throws Exception {
mockMvc.perform(post(CREATE_USER_URL)
// doesn't work either if I put "/v1/user/10" or post("/v1/user/{id}", 10) here
.content(TestUtils.toJson(request, false))
.contentType(contentType))
.andDo(print())
.andExpect(status().isCreated())
.andReturn();
}
}
If want to Test your real springboot url Test (End to end Test)
u can use rest-assured or resttemplte
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class)
#TestPropertySource(value={"classpath:application.properties"})
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
public class SpringRestControllerTest {
#Value("${server.port}")
int port;
#Test
public void getDataTest() {
get("/api/tdd/responseData").then().assertThat().body("data", equalTo("responseData"));
}
#Before
public void setBaseUri () {
RestAssured.port = port;
RestAssured.baseURI = "http://localhost"; // replace as appropriate
}
}
https://dzone.com/articles/test-driven-development-with-spring-boot-rest-api
Repository object not mocked from controller testcase return empty object here is the below code
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Main.class)
#WebAppConfiguration
#ActiveProfiles(ApplicationConstants.DEVELOPMENT_PROFILE)
public class EmployeeControllerRealTest {
#Autowired
private WebApplicationContext webAppContext;
private MockMvc mockMvc;
#Mock
EmployeeCompositeService employeeCompositeService;
String name = "mike";
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
MockitoAnnotations.initMocks(this);
}
#Test
public void testGetEmployees() throws Exception {
Mockito.when(employeeRepository.findByName(name)).thenReturn(getEmployees());
String url = URIConstants.ROOT_CONTEXT + URIConstants.EMPLOYEE;
MvcResult result =
mockMvc.perform(post(url)
.contentType(APPLICATION_JSON_UTF8)
.content(convertObjectToJsonBytes(name))
.andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$[0].employeeName").value("Mike"))
.andReturn();
String jsonContent = result.getResponse().getContentAsString();
LOGGER.debug("jsonContent: {}",jsonContent);
}
protected byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper.writeValueAsBytes(object);
}
private List<Employee> getEmployees(){
//here is the logic to get List of employees to return. When the mockito call is invoked.
}
}
I have a service call in side EmployeeController i.e employeeCompositeService.getEmployees(String name)
So I have mocked in the EmployeeControllerTestcase i.e #Mock EmployeeCompositeService employeeCompositeService;
when I run the controller testcase it invokes further services calls and repository and hit the database
So I does want to call those services returns the results form my employeeCompositeService.getEmployees(String name) from controller.
Can you please tell me what I did wrong in the above code Thanks in Advance
I'm working to test (via JUnit4 and Spring MockMvc) a REST service adapter using Spring-boot. The adapter simply passes along requests made to it, to another REST service (using a custom RestTemplate) and appends additional data to the responses.
I'd like to run MockMvc tests to perform controller integration tests, but want to override the RestTemplate in the controller with a mock to allow me to predefine the 3rd party REST response and prevent it from being hit during each test. I've been able to accomplish this by instantiating a MockMvcBuilders.standAloneSetup() and passing it the controller to be tested with the mock injected as listed in this post (and my setup below), however I am not able to do the same using MockMvcBuilders.webAppContextSetup().
I've been through a few other posts, none of which answer the question as to how this might be accomplished. I would like to use the actual Spring application context for the tests instead of a standalone to prevent any gaps as the application is likely to grow.
EDIT: I am using Mockito as my mocking framework and am trying to inject one of its mocks into the context. If this isn't necessary, all the better.
Controller:
#RestController
#RequestMapping(Constants.REQUEST_MAPPING_PATH)
public class Controller{
#Autowired
private DataProvider dp;
#Autowired
private RestTemplate template;
#RequestMapping(value = Constants.REQUEST_MAPPING_RESOURCE, method = RequestMethod.GET)
public Response getResponse(
#RequestParam(required = true) String data,
#RequestParam(required = false, defaultValue = "80") String minScore
) throws Exception {
Response resp = new Response();
// Set the request params from the client request
Map<String, String> parameters = new HashMap<String, String>();
parameters.put(Constants.PARAM_DATA, data);
parameters.put(Constants.PARAM_FORMAT, Constants.PARAMS_FORMAT.JSON);
resp = template.getForObject(Constants.RESTDATAPROVIDER_URL, Response.class, parameters);
if(resp.getError() == null){
resp.filterScoreLessThan(new BigDecimal(minScore));
new DataHandler(dp).populateData(resp.getData());
}
return resp;
}
}
Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringApplicationConfiguration(classes = MainSpringBootAdapter.class)
#TestPropertySource("/application-junit.properties")
public class WacControllerTest {
private static String controllerURL = Constants.REQUEST_MAPPING_PATH + Constants.REQUEST_MAPPING_RESOURCE + compressedParams_all;
private static String compressedParams_all = "?data={data}&minScore={minScore}";
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#InjectMocks
private Controller Controller;
#Mock
private RestTemplate rt;
#Value("${file}")
private String file;
#Spy
private DataProvider dp;
#Before
public void setup() throws Exception {
dp = new DataProvider(file);
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testGetResponse() throws Exception {
String[] strings = {"requestData", "100"};
Mockito.when(
rt.getForObject(Mockito.<String> any(), Mockito.<Class<Object>> any(), Mockito.<Map<String, ?>> any()))
.thenReturn(populateTestResponse());
mockMvc.perform(get(controllerURL, strings)
.accept(Constants.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultHandlers.print());
Mockito.verify(rt, Mockito.times(1)).getForObject(Mockito.<String> any(), Mockito.<Class<?>> any(), Mockito.<Map<String, ?>> any());
}
private Response populateTestResponse() {
Response resp = new Response();
resp.setScore(new BigDecimal(100));
resp.setData("Some Data");
return resp;
}
}
Spring's MockRestServiceServer is exactly what you're looking for.
Short description from javadoc of the class:
Main entry point for client-side REST testing. Used for tests that involve direct or indirect (through client code) use of the RestTemplate. Provides a way to set up fine-grained expectations on the requests that will be performed through the RestTemplate and a way to define the responses to send back removing the need for an actual running server.
Try to set up your test like this:
#WebAppConfiguration
#ContextConfiguration(classes = {YourSpringConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class ExampleResourceTest {
private MockMvc mockMvc;
private MockRestServiceServer mockRestServiceServer;
#Autowired
private WebApplicationContext wac;
#Autowired
private RestOperations restOperations;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
mockRestServiceServer = MockRestServiceServer.createServer((RestTemplate) restOperations);
}
#Test
public void testMyApiCall() throws Exception {
// Following line verifies that our code behind /api/my/endpoint made a REST PUT
// with expected parameters to remote service successfully
expectRestCallSuccess();
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/my/endpoint"))
.andExpect(status().isOk());
}
private void expectRestCallSuccess() {
mockRestServiceServer.expect(
requestTo("http://remote.rest.service/api/resource"))
.andExpect(method(PUT))
.andRespond(withSuccess("{\"message\": \"hello\"}", APPLICATION_JSON));
}
}
Here's another solution. Simply put, it just creates a new RestTemplate bean and overrides the one already registered.
So while it performs produces the same functionality as #mzc answer, it allows me to use Mockito to craft the response and verification matchers a bit easier.
Not that it's more than a couple lines of code, but it also prevents from having to add additional code to convert from the Response object to a string for the above mockRestServiceServer.expect().andRespond(<String>) method's arg.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringApplicationConfiguration(classes = MainSpringBootAdapter.class)
#TestPropertySource("/application-junit.properties")
public class WacControllerTest {
private static String Controller_URL = Constants.REQUEST_MAPPING_PATH + Constants.REQUEST_MAPPING_RESOURCE + compressedParams_all;
#Configuration
static class Config {
#Bean
#Primary
public RestTemplate restTemplateMock() {
return Mockito.mock(RestTemplate.class);
}
}
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#InjectMocks
private Controller Controller;
#Mock
private RestTemplate rt;
#Value("${file}")
private String file;
#Spy
private DataProvider dp;
#Before
public void setup() throws Exception {
dp = new DataProvider(file);
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
this.rt = (RestTemplate) this.wac.getBean("restTemplateMock");
}
#Test
public void testGetResponse() throws Exception {
String[] strings = {"request", "100"};
//Set the request params from the client request
Map<String, String> parameters = new HashMap<String, String>();
parameters.put(Constants.PARAM_SINGLELINE, strings[0]);
parameters.put(Constants.PARAM_FORMAT, Constants.PARAMS_FORMAT.JSON);
Mockito.when(
rt.getForObject(Mockito.<String> any(), Mockito.<Class<Object>> any(), Mockito.<Map<String, ?>> any()))
.thenReturn(populateTestResponse());
mockMvc.perform(get(Controller_URL, strings)
.accept(Constants.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultHandlers.print());
Mockito.verify(rt, Mockito.times(1)).getForObject(Mockito.<String> any(), Mockito.<Class<?>> any(), Mockito.<Map<String, ?>> any());
}
private Response populateTestResponse() {
Response resp = new Response();
resp.setScore(new BigDecimal(100));
resp.setData("Some Data");
return resp;
}
}
org.springframework.boot.test.mock.mockito.MockBean #MockBean helped me out.