Junit test controller with JSON body on POST - java

I am having issues creating a unit test for my LinuxCommandController. The LinuxCommandController returns a string from the responsebody of it's post mapping. It invokes LinuxCommandService which runs a command using the JSON string from the post body.
I've tried numerous ways to get this to work, my latest error is below. I've tried having a string returned as well, and get all types of errors. I feel like this shouldn't be this difficult, and I'm missing something simple.
Any help would be appreciated.
caservice: Compilation failure
[ERROR] /api/LinuxControllerTest.java:[59,55] cannot find symbol
[ERROR] symbol: method thenReturn(org.springframework.http.ResponseEntity)
[ERROR] location: class java.lang.String
Here is my junit test class.
package com.development.api;
import com.development.services.LinuxCommandService;
//import jdk.internal.net.http.ResponseBodyHandlers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
//import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.http.ResponseEntity;
import org.springframework.security.test.context.support.WithMockUser;
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.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.http.MediaType;
import static org.mockito.Mockito.when;
#RunWith(SpringRunner.class)
#WebMvcTest(LinuxController.class)
#WithMockUser
#AutoConfigureMockMvc
public class LinuxControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private LinuxCommandService linuxCommandService;
#InjectMocks
private LinuxController linuxController;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(linuxController)
.build();
}
#Test
public void test_that_the_jenkins_controller_received_all_required_params_and_our_service_returned_a_201_created() throws Exception {
String jsonValue = "{\"command\":\"CMD12}";
ResponseEntity linuxResponse = ResponseEntity.status(HttpStatus.CREATED)
.body(HttpStatus.CREATED);
when(linuxCommandService.runCommand(jsonValue).thenReturn(linuxResponse));
mockMvc.perform( MockMvcRequestBuilders
.post("/api/linux/command")
.content(jsonValue)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isCreated());
}
}
Here is the controller.
import com.services.LinuxCommandService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
#RestController
#NoArgsConstructor
#RequestMapping("/api/linux")
public class LinuxController {
#Autowired
LinuxCommandService linuxCommandService;
#PostMapping(path = "/command", consumes = "application/json", produces = "application/json")
#ResponseStatus(code = HttpStatus.CREATED)
public String create(#RequestBody #NotBlank String requestCommand) {
return linuxCommandService.runCommand(requestCommand);
}
}
And the service method returns a string,
public String runCommand(String requestCommand) {
.....
return "string returned";
}

when(linuxCommandService.runCommand(jsonValue))
.thenReturn(linuxResponse.toString());

Related

Spring Boot Unit Test returns 404 instead of 200

I am new in springboot. I am just watching the Spring in Action and programming follow the author.
then things get difficult when i just reading the chapter 1. I need to test a controller. the code in this book is:
package tacos;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
#RunWith(SpringRunner.class)
#WebMvcTest(HomeController.class) // <1>
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc; // <2>
#Test
public void testHomePage() throws Exception {
mockMvc.perform(get("/")) // <3>
.andExpect(status().isOk()) // <4>
.andExpect(view().name("home")) // <5>
.andExpect(content().string( // <6>
containsString("Welcome to...")));
}
}
I am using springboot 2.7.3 so i just remove #RunWith(SpringRunner.class) from my code. and i immediately get an error when do the test:
after google that i realized it might not find my controller so I add #Import(HomeController.class) to the test class. It looks like:
package com.qph.tacos;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
#WebMvcTest(HomeControllerTest.class)
#Import(HomeController.class)
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testHomePage() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("home"))
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("Welcome to...")));
}
}
then it just pass the test!!!
now i just wonder why the code written by the author can pass the test without #Import(HomeController.class)?
my directory structure is like:
the controller to be tested is:
package com.qph.tacos;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class HomeController {
#GetMapping("/")
public String home() {
// return the name of template
return "home";
}
}
I am new in springboot. maybe it's a simple question but i really spent so much time on it.
Thanks for your help!
Change to
#WebMvcTest(HomeController.class)
You should reference the controller under test with this annotation, otherwise it won't be loaded into the application context.

Integration test and mockWebServer

I try to run an integration test and mockMvc.perform(...) works (returns the actual response from https://jsonplaceholder.typicode.com/posts/1). However, in the PostService class, which is part of the integration test, a request should not actually be sent to https://jsonplaceholder.typicode.com/posts/1 during the integration test, but the request should go to the mockWebServer. How could this be implemented?
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.io.IOException;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#SpringBootTest
#AutoConfigureMockMvc
class PostControllerTest {
#Autowired
private MockMvc mockMvc;
MockWebServer mockWebServer;
#BeforeEach
void setUp() throws IOException {
mockWebServer = new MockWebServer();
mockWebServer.start();
}
#AfterEach
void tearDown() throws IOException {
mockWebServer.shutdown();
}
#Test
void integrationTestPost1() throws Exception {
mockWebServer.enqueue(
new MockResponse()
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody("{\"userid\": 123, \"id\": 456, \"title\": \"789\", \"body\": \"010\"}")
.setResponseCode(200));
// Gets actual response from https://jsonplaceholder.typicode.com/posts/1
// but should get response from mockWebServer
mockMvc.perform(
get("/post1"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.userid").value(123));
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
#RestController
public class PostController {
#Autowired
private PostService postService;
#GetMapping("/post1")
public Post getPost1() {
return postService.getPost1();
}
}
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
#Service
#Data
public class PostService {
private final HttpClient httpClient = HttpClient.create();
#Autowired
private WebClient.Builder webClientBuilder;
#RequestMapping("/post1")
public Post getPost1() {
return webClientBuilder
.clientConnector(new ReactorClientHttpConnector(httpClient))
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build()
.get()
.uri("https://jsonplaceholder.typicode.com/posts/1")
.retrieve()
.bodyToMono(Post.class)
.block();
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Post {
private int userid;
private int id;
private String title;
private String body;
}

Mock unit test a Service implementation that calls a Stored Procedure in Java

I’m trying to unit test a Service implementation that calls a Stored Procedure in Java.
My idea is to Mock or Stub the service just to see if all the calls are made correctly.
Not quite sure how to do this.
I’m using Springframework for the Service.
Any ideas on how to easily do this ? I'm getting null of course for the
#Autowired
private PerformanceService performanceService;
which doesn't initially happen, anyway I just want to Mock that and just make sure the calls are happening, thanks.
import com.integration.as400.entity.Performance;
import com.integration.as400.service.PerformanceService;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.BDDMockito.mock;
import static org.mockito.Mockito.when;
#Configuration
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes={PerformanceService.class, TestConfiguration.class})
#ActiveProfiles("test")
public class PerformanceServiceImplMockTest {
private static final Logger logger = LoggerFactory.getLogger(PerformanceServiceImplMockTest.class);
#Autowired
private PerformanceService performanceService;
#Before
public void setupMock(){
MockitoAnnotations.initMocks(this);
performanceService = mock(PerformanceService.class);
}
#Test
public void shouldReturnListPerformance_whenGetListPerformanceIsCalled() throws Exception{
List<Performance> performances = new ArrayList<>();
performances.add(new Performance("0000184","00001","MWR","SI",LocalDate.parse("2016-01-01"),LocalDate.parse("2016-01-31"),"CAD",48585.63,34821.01,47501.47,-94372.00,11184.70,10.000322,"",33105.91));
performances.add(new Performance("0000184","00001","MWR","SI",LocalDate.parse("2016-01-01"),LocalDate.parse("2016-01-31"),"CAD",142743.11,260376.38,41688.49,-4886.00,11184.70,35937.14,"",7.475078));
logger.info("Stubbing getListPerformance(int pageNumber, String loadStartDate, String loadEndDate) to return " + performances);
// Arrange
when(performanceService.getListPerformance(2, "2021-08-01", "2021-08-31")).thenReturn(performances);
// Act
List<Performance> retrievedPerformances = performanceService.getListPerformance(2, "2021-08-01", "2021-08-31");
// Assert
assertThat(retrievedPerformances, is(equalTo(performances)));
}
}

How to mock a service class calling Rest API and getting ResponseEntity as Response

I have below classes.
#Service
class RestService {
#Autowired
private RestTemplate restTemplate;
public ResponseEntity<String> callService(String param) {
return restTemplate.exchange(....);
}
}
#Service
class CallingService{
#Autowired
private RestService restService;
public ResponseDTO getResponse(String param) {
ResponseEntity<String> response = restService.callService(param);
ResponseDTO responseDTO = convert(response)// JSON Convertor here
return responseDTO;
}
}
Now I want to write test class for CallingService class.
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import javax.xml.datatype.DatatypeConfigurationException;
import org.apache.cxf.helpers.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.jupiter.api.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
class CallingServiceTest{
#InjectMocks
private CallingService service;
#Mock
private RestService restService;
#Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<String> responseEntity = new ResponseEntity<>("response message", header,
HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse("param");
//assert conditions here onwards
}
}
The invocation at line service.getResponse("param") gives NullPointerException. On debugging I found that response received in CallingService.getResponse() is null (i.e. restService.callService(param) returned null) and hence code broken into convert() method.
I tried with a lot of code manipulations but no luck. Hope anyone might have answer for this.
I finally found a workaround by combining flavours of above two answers.
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.apache.cxf.helpers.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
public class CallingServiceTest{
#InjectMocks
private CallingService service;
#Mock
private RestService restService;
#BeforeEach
public void setup(){
MockitoAnnotations.init(this);
}
#Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<String> responseEntity = new ResponseEntity<> ("response message", header, HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse("param");
//assert conditions here onwards
}
}
You can't use both #RunWith(MockitoJUnitRunner.class) and #SpringBootTest. They're both setting up JUnit Rules and you can only have one rule active.
You need to use Spring annotations. Instead of #Mock use #MockBean so Spring can properly inject it to the service.
#SpringBootTest
class CallingServiceTest{
#Autowired
private CallingService service;
#MockBean
private RestService restService;
#Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<String> responseEntity = new ResponseEntity<>("response message", header,
HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse("param");
//assert conditions here onwards
}
}
Alternatively, you can use just Mockito since you don't really need to initialize SpringContext for this test I think.
#RunWith(MockitoJUnitRunner.class)
class CallingServiceTest{
#InjectMocks
private CallingService service;
#Mock
private RestService restService;
#Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<String> responseEntity = new ResponseEntity<>("response message", header,
HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse("param");
//assert conditions here onwards
}
}
Based on your import you are using a combination of Junit4 and Junit5.
This will not work properly.
Steps to run with Junit5
Change your imports to org.junit.jupiter.api.*
Change #Before to #BeforeEach.
Remove #SpringBootTest not required with Junit5
Change #RunWith(MockitoJUnitRunner.class) to #ExtendWith(MockitoExtension.class)
Please find the changed code below
#ExtendWith(MockitoExtension.class)
class CallingServiceTest{
#InjectMocks
private CallingService service;
#Mock
private RestService restService;
#Test
public testGetResponse(){
ResponseDTO dto = createDummyObject();
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<String> responseEntity = new ResponseEntity<>("response message", header,
HttpStatus.OK);
when(restService.callService(Mockito.anyString())).thenReturn(responseEntity);
// mocking converter here
dto = service.getResponse("param");
//assert conditions here onwards
}
}
If you want to run with Junit4 change your import for #Test to import org.junit.Test from import org.junit.jupiter.api.Test
Either one of the solutions will work.

NoClassDefFoundError: org/springframework/web/bind/MissingMatrixVariableException -- testing repositories/interfaces

Here's the problem: I keep running into the MissingMatrixVariableException in my stacktrace when attempting to test my Repository interfaces. I've got 5 and want to increase my overall code coverage.
Be nice - I'm a noob. I've attempted to Mock after doing my own research, and it's been a bit overwhelming.
I've tried Mocking the repository and instantiated my MockMvc mockMvc variables. I've setUp the MockitoAnnotations.initMocks(this);
I run the test and for any testing I've done, I've hit the MissingMatrixVariableException error.
I've found nothing in Stack / Google searches for this Exception when testing repositories.
package example.repository;
import example.model.Metrics;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
#EnableJpaRepositories
public interface MetricsRepository extends JpaRepository<Metrics, Long> {
List<Metrics>findByDate(String date);
#Modifying
#Transactional
#Query(nativeQuery = true, value = "delete from backlogreport.metrics where date = ?1")
void deleteDate(String date);
}
This is my test class:
package example.repository;
import example.controller.RestApiController;
import example.model.Metrics;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder;
import java.util.Collections;
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;
public class MetricsRepositoryTest {
#InjectMocks
private RestApiController restApiController;
#Mock
private MetricsRepository MetricsRepository;
private MockMvc mockMvc;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(restApiController).build();
}
#Test
public void shouldGetByDate() throws Exception {
Metrics Metrics = new Metrics();
Metrics.setDate("2019-04-01");
Metrics.setAgeLength("20 days old");
when(MetricsRepository.findAll())
.thenReturn(Collections.singletonList(Metrics));
mockMvc.perform(get("/report/2019-04-01"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(print());
}
}
What I'm trying to achieve is when the mockMvc is setup, I put in the date which accepts a String ("2019-04-01") and an age length of ("20 Days").
I then perform my urL "get" and expect the status is ok, then print... except I keep hitting this MissingMatrixVariableException. Any ideas?

Categories

Resources