How to test POST method in Spring boot using Mockito and JUnit - unit-testing

I am newbie to Unit testing using JUnit and Mockito in Spring boot framework.
I want to test this method. How to test POST Request method:
// add Employee
#RequestMapping(method = RequestMethod.POST)
public void addEmployee(#RequestBody Employee employee){
this.employeeService.addEmployee(employee);
}
Thank you in advance

As #merve-sahin rightly pointed out, you can use #WebMvcTest to achieve this.
Look at the following example :
#RunWith(SpringRunner.class)
#WebMvcTest(YourController.class)
public class YourControllerTest {
#Autowired MockMvc mvc;
#MockBean EmployeeService employeeService;
#Test
public void addEmployeeTest() throws Exception {
Employee emp = createEmployee();
mvc.perform(post("/api/employee")
.contentType(MediaType.APPLICATION_JSON)
.content(toJson(emp)))
.andExpect(status().isOk());
}
}
In Above code you can mock your dependent service using #MockBean.
The test will perform post on your custom Employee object and validate the response
You can add headers, authorization while calling perform
Assuming you using JSON as media type, you can write toJson() method using any json library to convert Employee object into Json string format
private String toJson(Employee emp) {
If you are using XML, then you can do the same for XML
You can validate the response using expectations in chained way.
As rightly pointed out, please checkout MockedMvc link which should help you

Go through this following example:
#RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationControllerTest {
#Mock
EmployeeService employeeService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
initMocks(this);
YourController controller = new YourController(employeeService);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void addEmployee() throws Exception {
Employee emp = new Employee("emp_id","emp_name");//whichever data your entity class have
Mockito.when(employeeService.addEmployee(Mockito.any(Employee.class))).thenReturn(emp);
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.content(asJsonString(emp))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"));
}
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
In above given example mock your service class which is required to post the data to your Employee entity class.
I'm assuming that you're doing this via controller so you first need to initialize the controller which comes under the #Before annotation.
By doing above example you'll be able to post your data into the JSON format.

The below example is using JUnit5, Mockito3.x, spring-boot2.4.4, and
assertj3.x
The spring-boot-starter-test dependency from version 2.2.0
already comes with Junit 5 and contains also Hamcrest, assertj, and Mockito libraries.
In JUnit 5, “Runner” extension points, available in JUnit 4, are replaced by the Extension API.
You can register the Mockito extension via #ExtendWith.
Initializes mocks annotated with #Mock annotation so that explicit usage of MockitoAnnotations#initMocks(Object) is not required.
From spring-boot 2.1, there is no need to load the SpringExtension using annotation #ExtendWith because it's included as a meta-annotation in these annotations #DataJpaTest, #WebMvcTest, and #SpringBootTest.
Complete example with Github link: https://github.com/jdamit/DemoSpringBootApp.git
**#WebMvcTest(controllers = UserController.class)**
public class UserControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper mapper;
#MockBean
private UserServiceImpl userService;
private List<UserDto> users;
private UserDto user;
private String URI = "/users";
#BeforeEach
void setUp(){
users = List.of(new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"),
new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"),
new UserDto("Amit", "Kushwaha", "jdamit2027#gmail.com", "sector 120"));
user = new UserDto("Rahul", "Swagger", "rahul.swagger#gmail.com", "sector 120");
}
#Test
//#Disabled
void getUsersTest() throws Exception {
Mockito.when(userService.getUsers()).thenReturn(users);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(URI)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
Assertions.assertThat(result).isNotNull();
String userJson = result.getResponse().getContentAsString();
Assertions.assertThat(userJson).isEqualToIgnoringCase(mapper.writeValueAsString(users));
}
#Test
//#Disabled
void createUserTest() throws Exception {
Mockito.when(userService.createUser(Mockito.any(UserDto.class))).thenReturn(user);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(URI)
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(user).getBytes(StandardCharsets.UTF_8))
.accept(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
Assertions.assertThat(result).isNotNull();
String userJson = result.getResponse().getContentAsString();
Assertions.assertThat(userJson).isNotEmpty();
Assertions.assertThat(userJson).isEqualToIgnoringCase(mapper.writeValueAsString(user));
}
}

Related

***HELP*** Test/Mock: Service Class using Rest Template, Response and HTTP Entities, Exceptions

I have a service class, with for readability purpose, I have provided the code with dummy variables and objects. I am trying to write a JUNIT test class for the service, primarily with Mockito. No matter how hard I try, I am not able to hit the method serviceMethod irrespective of using spy/mock. I have also included a test, following the main class.
I know I am missing something here, but doesn't cross my mind. I need an eye to review this and let me know how I can write a proper test class for this and obtain coverage for the method.
(P.S. all the necessary imports are in-place and not pasted here to keep this concise)
Thanks in advance!
#Service
public class ServiceClass {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceClass.class);
#Autowired
String stringUrl;
RestTemplate restTemplate = new RestTemplate();
public void serviceMethod(ModelObject model) {
try {
HttpEntity<ModelObject> request = new HttpEntity<>(model);
ResponseEntity<String> response = restTemplate.exchange(stringUrl,
HttpMethod.POST, request, String.class);
LOGGER.info(response.getBody() + "and " + response.getStatusCode());
} catch (HttpClientErrorException exception) {
LOGGER.info(exception.getResponseBodyAsString());
} catch (HttpStatusCodeException exception) {
LOGGER.info(exception.getResponseBodyAsString());
}
}
Sample Test:
#RunWith(MockitoJUnitRunner.Silent.class)
public class ServiceClassTest {
#InjectMocks
private ServiceClass serviceClass;
#Mock
private RestTemplate restTemplate;
#Test
public void testServiceMethod() {
ServiceClass spy = Mockito.spy(serviceClass);
// ServiceClass spy = mock(ServiceClass.class, Mockito.CALLS_REAL_METHODS);
doNothing().when(spy).serviceMethod(Mockito.any(ModelObject.class));
Mockito.doCallRealMethod().when(spy).serviceMethod(Mockito.any(ModelObject.class));
ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.<HttpEntity<ModelObject>>any(), ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
}

Apache Camel Beans Unit Testing

I am using Apache Camel with Spring boot in my application. Currently I am working on a Unit test.
Java Code
DataRoute class
from("direct:getData")
.routeId("getData")
.bean(DataService.class, "processData")
.marshal().json(JsonLibrary.Jackson)
.end();
DataService class
public Data processData() {
return new Data("Hello World");
}
Data Class with getters, setters and Jackson toString method
private String value;
Unit test
BaseCamelContextUnitText
public abstract class BaseCamelContextUnitTest extends CamelTestSupport
{
#Autowired
private DataService dataService;
#Produce
private ProducerTemplate producerTemplate;
public CamelContext getCamelContext() {
return camelContext;
}
#Override
protected Context createJndiContext() throws Exception {
JndiContext context = new JndiContext();
context.bind("dataService", dataService);
return context;
}
#Test
public void shouldProcessData() throws Exception {
RouteDefinition routeDef = getCamelContext().getRouteDefinition("getData");
routeDef.adviceWith((ModelCamelContext) getCamelContext(), new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:getData")
.pipeline("bean:dataService?method=processData");
}
});
getCamelContext().start();
String responseData = "{"
+ "\"value\":\"Unit test success\""
+ "}";
Object response = producerTemplate.sendBody("direct:getData", ExchangePattern.InOut, null);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
((InputStreamCache) response).writeTo(byteArrayOutputStream);
assertThat(new String(byteArrayOutputStream.toByteArray()), is(responseData));
}
}
How do I mock
.bean(DataService.class, "processData")
in the unit test to return a mock Data Object with its default String variable as say "Unit test success" and then test to see if the route would give back the mocked Object instead of the Object with "Hello World" String variable?
This may seem's a late response, but I faced the same thing as described in your case, and I come across a simple way to "mock" the bean step, by using the DefaultRegistry to register the mocked bean into Camel registry, for example :
#Test
public void shouldProcessData() throws Exception {
...
...
DataService dataService = new DataService(...);
stubBean("dataService", dataService); // put this before calling context.start();
context.start();
...
...
}
/**
* This method inject a stub bean into the registry
* #param beanName the name of the bean being injected
* #param instance the stub instance
*/
void stubBean(String beanName, Object instance) {
DefaultRegistry registry = context.getRegistry(DefaultRegistry.class);
registry.bind(beanName, instance);
}
You can autowired the DataService in your DataRoute like
#Component
public class DataRoute extends RouteBuilder {
#Autowired
private DataService dataService;
#Override
public void configure() throws Exception {
from("direct:getData")
.routeId("getData")
.bean(dataService, "processData")
.marshal().json(JsonLibrary.Jackson)
.end();
}
}
So you can mock the DataService as usual.
An alternative is using beanRef("beanName", "methodName") in the Route.

Mockito mocks and ensuring method level spring security is tested [duplicate]

This question already has answers here:
Injecting Mockito mocks into a Spring bean
(23 answers)
Closed 7 years ago.
I'm trying to test some controller with method-level spring security and I want to mock out the repository dependencies in the controller. Basically I want to test that (a) the methods are enforcing security and (b) other beans invoked in SpEL expressions are working.
My issue is that when using Mockito's #InjectMocks for instantiating the controller the spring security proxies are not being applied to the controller and the method security is bypassed. If I use #Autowired to allow Spring to create the controller, my custom method level security logic does get called but the #Mock objects are not injected.
#RestController
#RequestMapping("/api/projects/{projectId}")
public class ProjectKeywordResource {
//I want to mock this repository
#Inject
private ProjectKeywordRepository projectKeywordRepository;
//Invokes another bean if user not assigned admin role.
#PreAuthorize("hasRole('ROLE_ADMIN')" + " or "
+ "#projectService.canEditProjectData(#projectId)")
#RequestMapping(value = "/projectKeywords", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<ProjectKeyword> create(
#PathVariable String projectId,
#RequestBody ProjectKeyword projectKeyword)
throws URISyntaxException {
projectKeywordRepository.save(projectKeyword);
return ResponseEntity.created(
new URI("/api/" + projectId + "projectKeywords/"
+ projectKeyword.getId())).body(result);
}
}
My Test Case is here:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class ProjectKeywordResourceSecurityTest {
private static final String DEFAULT_PROJECT_ID = "1";
#Mock
private ProjectKeywordRepository projectKeywordRepository;
//#Inject - Adding the following annotation adds the necessary spring security proxies,
but then ProjectKeywordResource uses the real ProjectKeywordRepository not the mock one.
#InjectMocks
private ProjectKeywordResource projectKeywordResource;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test(expected = AccessDeniedException.class)
#WithMockUser
#Transactional
public void testCreateThrowsAccessDenied() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
#Test
#WithMockUser(username = "admin", roles={"ADMIN"})
#Transactional
public void testCreateAuthorizationSuceedsForAdminUser() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
}
Is there a bit of config magic that allows me to wrap the Mockito mock controller with the necessary spring proxies, or alternatively force the use of the Mock on the injected bean in my test case?
The link that Bewusstsein posted got me on the right track to a viable answer posted by jfcorugedo. Basically what I had to do was to create a new bean in my test configuration class that mocks the Repository class and annotate it with the #Primary annotation. Adding the Spring profile annotation allows these beans to be switched off by default and therefore doesn't interfere with other tests. The revised test class is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#ActiveProfiles({"useMockRepositories","default"})
public class ProjectKeywordResourceSecurityTest {
private static final String DEFAULT_PROJECT_ID = "1";
#Inject
private ProjectKeywordResource projectKeywordResource;
#Test(expected = AccessDeniedException.class)
#WithMockUser
public void testCreateThrowsAccessDenied() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
#Test
#WithMockUser(username = "admin", roles={"ADMIN"})
public void testCreateAuthorizationSuceedsForAdminUser() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
My Test Configuration class has the following:
#Configuration
public class TestConfiguration {
#Profile("useMockRepositories")
#Bean
#Primary
public ProjectKeywordRepository MockProjectKeywordRepository() {
return Mockito.mock(ProjectKeywordRepository.class);
}
}

Not able to inject mock objects

I am newe to Mockito and Junit, I have written unit test cases for testing my rest service and made use of Mockito for injecting mocks. And code is below:
BillControllerTest.java:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class BillControllerTest{
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#InjectMocks
private BillController billController;
#Mock
private BillService mockBillService;
#Before
public void setupController() {
MockitoAnnotations.initMocks(this);
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testBills() throws Exception {
// some fake data
final List<Bill> fakeBillList = new ArrayList<>();
fakeBillList.add(CpsFake.bill("1234"));
when(mockBillService.getBills(BILL_UID))
.thenReturn(fakeBillList.stream());
mockMvc.perform(get("/bills/" + BILL_UID ))
.andExpect(content().contentType(MediaTypes.HAL_JSON))
// expect particular uid
.andExpect(content().string(containsString("\"uid\":\"1234\"")))
ApplicationTest.java:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class ApplicationTest {
#Test
public void contextLoads() {
}
}
BillController.java:
#RestController
#RequestMapping(value = "/trials/{billUid}", produces = "application/hal+json")
public class BillController extends BaseController {
#Autowired
private BillService billService;
#Autowired
public BillController(BillService billService) {
this.billService = billService;
}
#RequestMapping(method = RequestMethod.GET, value = "")
public ResponseEntity<Resources<Resource<Bill>>> getBills(#PathVariable String billUid) {
return resourceListResponseEntity(
() -> billService.getBills(billUid),
bill-> createResource(bill),
resources -> resources.add(linkTo(methodOn(BillController.class)
.getBills(billUid)).withSelfRel()));
}
When I run the test (BillControllerTest), mockBillService is not getting invoked and instead it is calling actual billService. Please help me in this issue. Thank you in advance.
I think the problem is that you use mockito together with spring. Both make use of proxys.
Looking at your code of getBills - it is not dependent on the spring application context. So skip all your spring setup code (mockMvc and webApplicationContext) and use only Mockito. If yet invisible code depends on the ApplicationContext - mock the application context rather than setting up a real one.
This test would be:
simpler
container independent
faster
You could replace initMocks with the Annotation RunWith(MockitoJUnitRunner.class) if you want.

Mock object is not injected

I try mock controler:
#RestController
public class SthControl {
#Autowired
private ObjRepo repo;
#RequestMapping(value = "/dosth", method = RequestMethod.POST, produces = "application/json")
public ModelMap handleSth(#RequestParam("key") String key) {
final Logger logger = Logger.getLogger(getClass());
logger.info("Is Mock "+ new MockUtil().isMock(repo));//return FALSE- is real object
logger.info("Key " + repo.loadByKey(key);//return NULL- always call real Method
Test Case:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("file:src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml")
#WebAppConfiguration
public class SthControlTest {
#SuppressWarnings("SpringJavaAutowiringInspection")
#Autowired
protected WebApplicationContext wac;
private MockMvc mockMvc;
#Mock
private ObjRepo repo;
#InjectMocks
#Autowired
private SthControl contr;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = webAppContextSetup(this.wac).build();
BasicConfigurator.configure();
}
#Test
public void testRegister() throws Exception {
final UUID uuid = UUID.randomUUID();
final String keyValue = "KeyVal";
final Logger logger = Logger.getLogger(getClass());
repo = Mockito.mock(ObjtRepo.class);
Mockito.when(repo.loadByKey(keyValue)).thenReturn(new Obj(uuid, keyValue, TimeUtils.currentTimeSecond(), false));
Problem still exist if replace #Mock Annotation with this lines
repo = Mockito.mock(ObjRepo.class);
ReflectionTestUtils.setField(contr, "repo", repo, ObjRepo.class);
logger.info("Obj " + repo.loadByKey(keyValue).getId());//return correct object
logger.info("Mock Is "+new MockUtil().isMock(ReflectionTestUtils.getField(contr,"repo")));//True
There are two issues here.
You need to swap the lines this.mockMvc = webAppContextSetup(this.wac).build(); and MockitoAnnotations.initMocks(this);, otherwise the injection done by the Spring web application context setup will overwrite the injection done by Mockito. Always do the Spring injection first.
You need to remove the line repo = Mockito.mock(ObjtRepo.class); from testRegister, because this line replaces the value in repo with one that differs from the one you injected, so when you stub the new value, it won't affect the behaviour of SthControl.