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;
}
Related
I have a method that evicts all the caches. PFB code for same:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import com.admin.AdminResponse;
#Service
public class CachingService {
private final static Logger logger = LoggerFactory.getLogger(CachingService.class);
#Autowired
protected CacheManager cacheManager;
public AdminResponse evictAllCaches() {
logger.info("Start - Clearing of cache");
cacheManager.getCacheNames().parallelStream()
.forEach(cacheName -> cacheManager.getCache(cacheName).clear());
AdminResponse adminResponse = new AdminResponse();
adminResponse.setMessage("ok");
logger.info("End - Clearing of cache");
return adminResponse;
}
}
Below is the unit test I'm trying to write:
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.admin.AdminResponse;
#ExtendWith(SpringExtension.class)
public class CachingServiceTest {
#InjectMocks
private CachingService testCachingService;
#Mock
protected CacheManager cacheManager;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testEvictAllCaches() {
AdminResponse adminResponse = testCachingService.evictAllCaches();
assertEquals("ok", adminResponse.getMessage());
}
}
I'm unable to understand how to write unit tests for code
cacheManager.getCacheNames().parallelStream()
.forEach(cacheName -> cacheManager.getCache(cacheName).clear());
Can someone please help? Thank you for your time!
You can write test code for Cache as follow:
#Test
public void testEvictAllCaches() {
Cache cache = Mockito.mock(Cache.class);
when(cacheManager.getCacheNames()).thenReturn(List.of("cacheName1", "cacheName2"));
Mockito.when(cacheManager.getCache(anyString())).thenReturn(cache);
AdminResponse adminResponse = testCachingService.evictAllCaches();
assertEquals("ok", adminResponse.getMessage());
}
URL : http://X.X.X.X/APPLICATION/abc?a=1
Is my URL correct?
JAVA Code:
#RequestMapping(value = "/abc?", method = RequestMethod.GET)
#ResponseBody
public ModelAndView page (#RequestParam(value = "a") String a) {
...
Code Logic
...
}
Am I handling it correctly?
Remove the question mark after abc.
value = "/abc"
this is how I am handling the url params. I have also attached the test code below to make it easier to understand.
Here's my controller
package com.example.demo;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/APPLICATION")
public class DemoController {
#GetMapping("/abc")
public #ResponseBody String getAbc(#RequestParam String a) {
return a;
}
}
And here's the test code
package com.example.demo;
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.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.equalTo;
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.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#SpringBootTest
#AutoConfigureMockMvc
class DemoApplicationTests {
#Autowired
private MockMvc mockMvc;
#Test
void contextLoads() {
}
#Test
public void shouldGetParamFromUrl() throws Exception {
this.mockMvc.perform(get("/APPLICATION/abc?a=1"))
.andDo(print()).andExpect(status().isOk())
.andExpect(content().string(equalTo("1")));
this.mockMvc.perform(get("/APPLICATION/abc?a=99"))
.andDo(print()).andExpect(status().isOk())
.andExpect(content().string(equalTo("99")));
}
}
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());
I am writing JUnits for controller classes. I am using #PropertySource("classpath:webmvc_test.properties") and Environment object to read the values from properties file. On calling getProperty() method getting null value. The property file webmvc_test.properties is under the class path.
TestClass.java:
package com.kalavakuri.webmvc.web.controller;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.kalavakuri.webmvc.business.service.FamilyService;
import com.kalavakuri.webmvc.business.valueobject.FamilyAddress;
import com.kalavakuri.webmvc.business.valueobject.FamilyVO;
import com.kalavakuri.webmvc.init.ApplicationInitializer;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { ApplicationInitializer.class })
#PropertySource("classpath:webmvc_test.properties")
public class WelcomeControllerTest {
#Mock
private FamilyService familyService;
#InjectMocks
private WelcomeController welcomeController;
#Autowired
private Environment environment;
private MockMvc mockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(welcomeController).build();
}
#Test
public void welcomePage() throws Exception {
FamilyVO allFamilyMembers = getAllFamilyMembers();
when(familyService.getAllFamilyMembers()).thenReturn(allFamilyMembers);
mockMvc.perform(get("/")).andExpect(status().isOk()).andExpect(view().name("Index"));
}
/**
* #return
*/
private FamilyVO getAllFamilyMembers() {
FamilyVO allFamilyMembers = new FamilyVO();
FamilyVO familyVO = new FamilyVO();
familyVO.setFamilyId(Integer.parseInt(environment.getProperty("familyId")));
familyVO.setFamilyMemberName(environment.getProperty("familyMemberName"));
familyVO.setFamilyMemberAge(Integer.parseInt(environment.getProperty("familyMemberAge")));
FamilyAddress familyAddress = new FamilyAddress();
familyAddress.setAddress(environment.getProperty("familyAddress"));
familyVO.setFamilyAddress(familyAddress);
List<FamilyVO> familyVOs = new ArrayList<FamilyVO>();
familyVOs.add(familyVO);
allFamilyMembers.setFamilyVOs(familyVOs);
return allFamilyMembers;
}
}
webmvc_test.properties:
familyId=1
familyMemberName=Ramachandrappa Kalavakuri
familyMemberAge=36
familyAddress=Flat no: 305, 2nd Floor, Prakasa Pride Apartments, Opp To J.P.Morgan, Kadubesinahalli, Bangalore - 560087
I had the same problem and when I searched for a solution for it I found this article #Autowired + PowerMock: Fixing Some Spring Framework Misuse/Abuse it seems that there is a design problem between powermock and spring that prevent #Autowire from working correctly inside test classes, So instead of using #Autowire use #Mock and expect the returned values
package com.kalavakuri.webmvc.web.controller;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.kalavakuri.webmvc.business.service.FamilyService;
import com.kalavakuri.webmvc.business.valueobject.FamilyAddress;
import com.kalavakuri.webmvc.business.valueobject.FamilyVO;
import com.kalavakuri.webmvc.init.ApplicationInitializer;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { ApplicationInitializer.class })
#PropertySource("classpath:webmvc_test.properties")
public class WelcomeControllerTest {
#Mock
private FamilyService familyService;
#InjectMocks
private WelcomeController welcomeController;
#Mock
private Environment environment;
private MockMvc mockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(welcomeController).build();
when(environment.getProperty("familyId")).thenReturn("1");
when(environment.getProperty("familyMemberName")).thenReturn("Ramachandrappa Kalavakuri");
when(environment.getProperty("familyMemberAge")).thenReturn("36");
when(environment.getProperty("familyAddress")).thenReturn("Flat no: 305, 2nd Floor, Prakasa Pride Apartments, Opp To J.P.Morgan, Kadubesinahalli, Bangalore - 560087");
}
#Test
public void welcomePage() throws Exception {
FamilyVO allFamilyMembers = getAllFamilyMembers();
when(familyService.getAllFamilyMembers()).thenReturn(allFamilyMembers);
mockMvc.perform(get("/")).andExpect(status().isOk()).andExpect(view().name("Index"));
}
/**
* #return
*/
private FamilyVO getAllFamilyMembers() {
FamilyVO allFamilyMembers = new FamilyVO();
FamilyVO familyVO = new FamilyVO();
familyVO.setFamilyId(Integer.parseInt(environment.getProperty("familyId")));
familyVO.setFamilyMemberName(environment.getProperty("familyMemberName"));
familyVO.setFamilyMemberAge(Integer.parseInt(environment.getProperty("familyMemberAge")));
FamilyAddress familyAddress = new FamilyAddress();
familyAddress.setAddress(environment.getProperty("familyAddress"));
familyVO.setFamilyAddress(familyAddress);
List<FamilyVO> familyVOs = new ArrayList<FamilyVO>();
familyVOs.add(familyVO);
allFamilyMembers.setFamilyVOs(familyVOs);
return allFamilyMembers;
}
}
I have a simple PersonController class that provides save() method to persist the object from http post request.
package org.rw.controller;
import java.sql.Timestamp;
import java.util.List;
import org.rw.entity.Person;
import org.rw.service.PersonService;
import org.rw.spring.propertyeditor.TimestampPropertyEditor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping(value="/person")
public class PersonController {
private static final Logger logger = LoggerFactory.getLogger(PersonController.class);
#Autowired
private PersonService personService;
#Autowired
TimestampPropertyEditor timestampPropertyEditor;
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Timestamp.class, "dob", timestampPropertyEditor);
}
#RequestMapping(value="/save", method=RequestMethod.POST)
public String save(Model model, Person person) {
Long personId = personService.save(person);
return "redirect:view/" + personId;
}
}
As the save() method returns as return "redirect:view/" + personId;. It will be diffrerent for every request. it may be like "view/5" or "view/6" depending on the id of the object that has been persisted.
Then i have a simple class to test the above controller with spring mocking.
package org.rw.controller;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.rw.service.UserService;
import org.rw.test.SpringControllerTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
public class PersonControllerTest extends SpringControllerTest {
#Autowired
private UserService userService;
#Test
public void add() throws Exception {
mockMvc.perform(get("/person/add", new Object[0])).andExpect(status().isOk());
}
#Test
public void save() throws Exception {
UserDetails userDetails = userService.findByUsername("anil");
Authentication authToken = new UsernamePasswordAuthenticationToken (userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
mockMvc.perform(
post("/person/save", new Object[0])
.param("firstName", "JunitFN")
.param("lastName", "JunitLN")
.param("gender", "M")
.param("dob", "11/02/1989")
).andExpect(
redirectedUrl("view")
);
}
}
now here i have a problem that redirectedUrl("view") is rejecting value "view/5". I have tried redirectedUrl("view*") and redirectedUrl("view/*") but its not working.
Edit :
Here I have got a workaround as per below
MvcResult result = mockMvc.perform(
post("/person/save", new Object[0])
.param("firstName", "JunitFN")
.param("lastName", "JunitLN")
.param("gender", "MALE")
.param("dob", "11/02/1989")
).andExpect(
//redirectedUrl("view")
status().isMovedTemporarily()
).andReturn();
MockHttpServletResponse response = result.getResponse();
String location = response.getHeader("Location");
Pattern pattern = Pattern.compile("\\Aview/[0-9]+\\z");
assertTrue(pattern.matcher(location).find());
but still i am looking for the proper way.
update:
I have posted the same issue on spring jira here :
Since spring 4.0 you can use redirectedUrlPattern as pointed by Paulius Matulionis
As of spring 3.x this is not supported out of the box but you can easily add you custom result matcher
private static ResultMatcher redirectedUrlPattern(final String expectedUrlPattern) {
return new ResultMatcher() {
public void match(MvcResult result) {
Pattern pattern = Pattern.compile("\\A" + expectedUrlPattern + "\\z");
assertTrue(pattern.matcher(result.getResponse().getRedirectedUrl()).find());
}
};
}
And use it like build-in matcher
mockMvc.perform(
post("/person/save", new Object[0])
.param("firstName", "JunitFN")
.param("lastName", "JunitLN")
.param("gender", "M")
.param("dob", "11/02/1989")
).andExpect(
redirectedUrlPattern("view/[0-9]+")
);
Since 4.0 it is available in Spring itself.
Please check here.