I am learning JUnit and mockito. I am trying to write test case for search filter from Account controller class. But I am getting Argument(s) are different! Wanted: failure . can any one tell me what I am doing wrong in Test case?
#RestController
#RequestMapping("/api.spacestudy.com/SpaceStudy/Admin")
public class AccountController {
#Autowired
AccountService accService;
#GetMapping("/Account/findAccountData")
public ResponseEntity<List<Tuple>> btnSearchClick(String sClientAcctId, String sAcctDesc, String sInvestigatorName,
String sClientDeptId) throws Exception {
return ResponseEntity.ok(accService.btnSearchClick("1124100", sAcctDesc,sInvestigatorName,sClientDeptId));
}
}
Test case
#RunWith(SpringRunner.class)
public class AccountControllerTest {
private MockMvc mockMvc;
#Mock
private AccountService accountService;
#InjectMocks
private AccountController accountController;
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(accountController).build();
}
#Test
public void btnSearchClickTest() throws Exception {
String sClientAcctId = "1124100";
String sAcctDesc = "SRIRAM";
String sInvestigatorName = "Ram, Sri";
String sClientDeptId = "120010";
Tuple mockedTuple = Mockito.mock(Tuple.class);
List<Tuple> accountObj = new ArrayList<>();
accountObj.add(mockedTuple);
Mockito.when(accountService.btnSearchClick(sClientAcctId, sAcctDesc, sInvestigatorName, sClientDeptId))
.thenReturn(accountObj);
mockMvc.perform(
get("/api.spacestudy.com/SpaceStudy/Admin/Account/findAccountData").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
Mockito.verify(accountService).btnSearchClick(sClientAcctId, sAcctDesc, sInvestigatorName, sClientDeptId);
}
}
Stack Trace
Argument(s) are different! Wanted:
accountService.btnSearchClick(
"1124100",
"SRIRAM",
"Ram, Sri",
"120010"
);
-> at com.spacestudy.controller.AccountControllerTest.btnSearchClickTest(AccountControllerTest.java:110)
Actual invocation has different arguments:
accountService.btnSearchClick(
null,
null,
null,
null
);
-> at com.spacestudy.controller.AccountController.btnSearchClick(AccountController.java:36)
Incase anyone still has this problem, I got a working solution from here
Essentially, you just need to override the Object.equals(Object) in most cases and if that does not work, maybe because the object you are validating cannot be changed or its equals function can not be overriden (for whatever reason which is beyond the context of this post), then use org.mockito.Matchers.refEq(T value, String... excludeFields) like below:
verify(programServiceMock, times(1)).save(id, refEq(testpPList));
Related
public class DummyTest {
#InjectMocks
private TestService service;
#Mock
private TestRepository repository;
#BeforeEach
public void before() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testMe() {
when(repository.findAllByQuery("")).thenReturn(List.of());
List<String> entities = service.findAllByQuery("test");
verify(repository, times(1)).findAllByQuery(anyString());
}
}
class TestService {
private TestRepository repository;
public List<String> findAllByQuery(String query) {
return repository.findAllByQuery(query);
}
}
class TestRepository {
public List<String> findAllByQuery(String query) {
return List.of("");
}
}
The test above successfully passed when I do Mockito initialization via #BeforeEach annotation.
After, I tried to remove #BeforeEach annotation and initialize Mockito with #ExtendWith(MockitoExtension.class) and the test failed due to:
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at DummyTest.testMe(DummyTest.java:33)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
Could you please explain to me, why I got so different test execution results? Technically, I have just changed the way to initialize Mockito and that all.
#ExtendWith(MockitoExtension.class) and MockitoAnnotations.initMocks(this) configures Mockito in a little different way.
When you use #ExtendWith(MockitoExtension.class) additionally configures "strictness" of Mockito with default value STRICT_STUBS.
Which means you have to change the configuration of you stubs like this:
#Test
public void testMe() {
when(repository.findAllByQuery(anyString())).thenReturn(List.of());
List<String> entities = service.findAllByQuery("test");
verify(repository, times(1)).findAllByQuery(anyString());
}
or even like this:
#Test
public void testMe() {
when(repository.findAllByQuery(eq("test"))).thenReturn(List.of());
List<String> entities = service.findAllByQuery("test");
verify(repository, times(1)).findAllByQuery(anyString());
}
adding test cases for getStudent method, this is having internal calls
1- is repository call - stubbing this call working fine
2- validate user call - stubbing this call not working, showing some error and test case failed.
Service Class
#Service
public class StudentServiceImpl implements StudentService {
#Autowired
FakeStudentRepository fakeStudentRepository;
#Override
public Optional<Student> getStudent(int id) {
Optional<Student> student = fakeStudentRepository.getStudent(id);
boolean isValid = myClass().isValidUser(student.get().getId());
if(!isValid) {
return Optional.empty();
}
return student;
}
public MyTestClass myClass() {
return new MyTestClass();
}
}
MyTestClass
public class MyTestClass {
public boolean isValidUser(int id) {
return true;
}
}
Test Class
#SpringBootTest
class StudentServiceImplTest {
#Mock
FakeStudentRepository fakeStudentRepository;
#InjectMocks
StudentServiceImpl studentServiceImpl;
#BeforeEach
public void setup() {
studentServiceImpl = Mockito.spy(StudentServiceImpl.class);
MockitoAnnotations.initMocks(this);
}
#Test
void getStudent() {
Optional<Student> student = Optional.of(Student.builder().id(1).firstName("Rahul").lastName("rahul")
.mobile("XXXXXX").build());
Mockito.doReturn(student)
.when(fakeStudentRepository).getStudent(ArgumentMatchers.anyInt());
Mockito.doReturn(false)
.when(studentServiceImpl).myClass().isValidUser(ArgumentMatchers.anyInt());
Optional<Student> resultStudent = studentServiceImpl.getStudent(student.get().getId());
assertEquals(resultStudent.get().getId(), student.get().getId());
}
}
Error
org.mockito.exceptions.misusing.WrongTypeOfReturnValue: Boolean
cannot be returned by myClass() myClass() should return MyTestClass
If you're unsure why you're getting above error read on. Due to the
nature of the syntax above problem might occur because:
1. This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency
testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
The error Message says it: You are mocking studentServiceImpl.myClass() and try to return true. It’s not possible to mock the end of a call chain as you try with your second Mockito expression.
To do what you want requires to mock myClass() first by returning a mocked class instance and mock isValidUser on that.
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));
}
}
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.
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.