Unable to mock the void method of Spring Data Jpa - java

I've below void method which I am looking to get mock of.
public void updateEmployee(EmployeeDto dto) {
Employee d = convertToEntity(dto);
employeeRepository.updateEmployee(d.getEmployeeName(), d.getEmployeeDescription(),
d.getEmployeeOwnerEmployeeId(), d.getEmployeeCode(), d.getStatus());
}
But I am getting below error.
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to when() is not a mock!
Example of correct stubbing:
doThrow(new RuntimeException()).when(mock).someMethod();
at com.xxx.EmployeeServiceTest.test_UpdateEmployee(EmployeeServiceTest.java:120)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Test method
#RunWith(PowerMockRunner.class)
#PrepareForTest({StatusEnum.class})
public class EmployeeServiceTest {
#Mock
private Employee employeeMock;
#InjectMocks
private EmployeeServiceImpl employeeServiceImpl;
#Mock
private EmployeeRepository employeeRepositoryMock;
#Mock
private EmployeeDto employeeDtoMock;
#Mock
private StatusEnum statusEnum;
#Mock
private Exception ex;
List<String> employeeNames = new ArrayList<>();
#Before
public void setup() {
// To mock static methods or class
mockStatic(StatusEnum.class);
}
#Test
public void test_UpdateEmployee() {
doNothing().when(employeeServiceImpl).saveEmployee(any(EmployeeDto.class));
employeeServiceImpl.updateEmployee(employeeDtoMock);
/*doAnswer((i) -> {
System.out.println("Employee setName Argument = " + i.getArgument(0));
assertTrue("Pankaj".equals(i.getArgument(0)));
return null;
}).when(employeeServiceImpl).updateEmployee(employeeDtoMock);*/
}
}

The exception here seem clear to me, employeeServiceImpl seems not to be a mock.
How did you instantiate it ? Using Mockito.mock or #Mock on the field ?
--- Edit
To clarify my answer, in the #Before (or equivalent in your test class) instantiate your service with mockito :
this.employeeService = Mockito.mock(EmployeeService.class);
or
#Mock
private EmployeeService employeeService;
And then it should work.
--- Edit
So seeing how you inject your mock I think I might get what's happening.
In fact #InjectMocks does not make your EmployeeService a mock. It allows mockito to know let your framework create the bean and injects the mocks you have created in it.
Here if you have declared your repository as a mock like this
#Mock
private EmployeeRepository employeeRepository;
Then a mock of type EmployeeRepository will be injected inside the employeeService instance, which is not a mock.
Then if it is in fact the repository you want to mock, you should put it in the when in your tests like :
doNothing().when(employeeRepository).saveEmployee(any(EmployeeDto.class));
employeeServiceImpl.saveEmployee(employeeDtoMock);
Then calling your service will go into your service saveEmployee function, but when it would reach the repository which is a mock it would work as expected.
If it is in fact the whole service you want to mock then instantiate it using #Mock instead of #InjectMocks.

Related

Spring injecting an initiallized mock via constructor

I have a singleton class (so private constructor) which needs to use a Spring Data repository during initialization. I have one injected as a constructor argument. Roughly:
#Controller
public class MyClass {
#Autowired
private MyClass(MyRepository repo) {
repo.findAll();
}
}
I want to unit test my class, so I need to have a mock repository initialized with mock values and then passed into my class before my class is initialized. How do I write my Mockito mocks in my JUnit test to make this possible?
You don't need Spring; this is an advantage of constructor injection. Just use MyRepository mockRepo = mock(MyRepository.class) and new MyClass(mockRepo).
(Your constructor should be public, by the way. You seem to be making the common mistake of confusing different senses of "singleton"; in the case of DI it simply means that the container only makes a single instance and shares it. Finally, if you only have one constructor you don't need #Autowired.)
Unit test should be independent. It means, we are not using real data from database even call any service from our test file.
Assuming you are using JUni5 and have findAllStudents() in your controller. So your test file approximately like this
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MyClassTest {
#Mock
private MyRepository repo;
#InjectMocks
private MyClass controller;
#BeforeAll
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testDataIsExist() {
List<String> expectednames = new ArrayList();
expectedNames.add("Foo");
expectedNames.add("Bar");
Mockito.when(myRepo.findAll()).thenReturn(expectedNames);
List<String> result = controller.findAllStudents();
Assertions.assertNotNull(result);
Assertions.assertEquals(expectednames, result);
}
}
So we are mock all the services we use in controller, then inject to controller itself. Then inside the test method we mock repo.findAll() to return expectedNames so if the controller find that function it will return what mock says to return.
After we call the function, we have to make it sure that the result is according to what we expected.
There is little value in a "unit test" for a #Controller implementation. If you use #WebMvcTest, then you can use #MockBean:
#WebMvcTest
class MyControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private MyRepository repository;
#Test
void testSomething() {
mockMvc.perform( ... );
}
}

#MockBean is not returning the expected object

below is my controller:
#RestController
#RequestMapping("/Employee")
public class Employeecontroller {
#Autowired
Employeeservice empservice;
#PostMapping("/addEmployee") // adding emp details into the table
public Employee addemployee(#RequestBody Employee emp)
{
empservice.saveemp(emp);
return emp;
}
}
this is the empservice class:
#Service
public class Employeeservice {
#Autowired
EmployeeRepository emprepo;
public Employee saveemp(Employee emp) {
System.out.println("inside save emp");
return emprepo.save(emp);
}
}
(here i dont want to call to emprepo.save(emp) method, which is a database call, so i used Mockito.when and then methods in below test class)
below is the test class:
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
#AutoConfigureMockMvc
class RestServiceApplicationTests {
#Autowired
private MockMvc mvc;
#Autowired
ObjectMapper objectMapper;
#MockBean
Employeeservice empservice;
Employee reqemp = new Employee("Bob", "java");
#Test
public void testaddemp() throws Exception {
when(empservice.saveemp(reqemp)).thenReturn(new Employee(1, "Bob", "java"));
RequestBuilder request = MockMvcRequestBuilders.post("/Employee/addEmployee")
.contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(reqemp));
MockHttpServletResponse returnedemp = (MockHttpServletResponse) mvc.perform(request).andExpect(status().isOk())
.andReturn().getResponse();
Employee expectedemp = new Employee(1, "Bob", "java");
assertEquals(objectMapper.writeValueAsString(expectedemp), returnedemp.getContentAsString());
}
}
Testcase failed with:
org.opentest4j.AssertionFailedError: expected: <{"id":1,"name":"Bob","tech":"java"}> but was: <{"id":0,"name":"Bob","tech":"java"}>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1124)
at com.easytocourse.demo.RestServicewithActuatorApplicationTests.testaddemp(RestServicewithActuatorApplicationTests.java:55)
when i use #Mock or #SpyBean it is returning expected employee object
Please help me in understanding why #MockBean is not working?
Please clarify the below points
1. what is the difference between #Mock, #MockBean, #SpyBean, #InjectMock annotations, when to use these annotations?
#MockBean is a feature of Spring Boot Test.
Thus you need to remove the #RunWith(MockitoJUnitRunner.class) declaration which is using Mockito's JUnit 4 Runner.
If you want to use JUnit 4, you must use the SpringRunner via #RunWith(SpringRunner.class).
If you want to use JUnit Jupiter (part of JUnit 5), you'll need to use the SpringExtension via #ExtendWith(SpringExtension.class).
Depending on the version of Spring Boot Test that you are using, you may be able to exclude the #ExtendWith(SpringExtension.class) declaration, since recent versions of #SpringBootTest automatically register the SpringExtension for you.
Related topic: Difference between #Mock, #MockBean and Mockito.mock()

Mockito: mocking a method of same class called by method under test when using #InjectMocks

I have a class I want to test that has several external dependencies, and a couple internal methods. I would like to write a test for MethodA, but not have Method A's internal call to MethodB to actually exercise MethodB. I'd like to mock/stub MethodB and return something specific instead. Usually I'd use when/thenReturn but it doesn't behave like I expect - it actually jumps into Method B while creating the mock itself.
MyService.java
#Service
public class MyService {
#Autowired
private ServiceA serviceA;
#Autowired
private ServiceB serviceB;
public SomeObject methodA() {
// some logic using serviceA.method and serviceB.method that creates "output"
SomeObject someObject = methodB(output);
return someObject;
}
public SomeObject methodB(SomeObject someObject) {
// deep mysteries done here to someObject
return someObject
}
}
MyServiceTest.java
public class MyServiceTest {
#Mock
private ServiceA serviceA;
#Mock
private ServiceB serviceB;
#InjectMocks
private MyService myService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void methodATest() {
when(serviceA.method()).thenReturn(stuff);
when(serviceB.method()).thenReturn(otherStuff);
// here is what I would like to do
when(myService.methodB()).thenReturn(mockedSomeObject); //<- doesn't work
assertThat(myService.methodA().getSomeObjectProperty())
.isEqualTo("property");
}
}
I've looked at solutions that manually mock the MyService class with Mockito.mock(MyService.class), but (as the above example is obviously contrived) my actual class has quite a few external dependencies and I'd prefer a solution that still allows me to mock the service using #Mock for the #Autowired dependencies and #InitMocks for the class under test, unless it's simply not possible.
I've tried:
Mockito.doReturn(mockedSomeObject).when(myService.methodB(any(SomeObject.class));
but that also steps into MethodB when creating the mock for that method, which shouldn't be happening.
Thanks in advance for the help!
Try Adding #Spy to your InjectMocks and use the object to "expect" them in a slightly different syntax.
import org.mockito.Spy;
#InjectMocks
#Spy
private MyService myService;
And now mock the service call
Mockito.doReturn(mockedSomeObject).when(myService).methodB();
Also change the other mock call to this
Mockito.doReturn(stuff).when(serviceA).method();
Mockito.doReturn(otherStuff).when(serviceB).method();
You need to mark your object as Spy or explicitly create a Spy object for it using MyClass objA=null;
MyClass spy_objA=Powermockito.spy(objA)
doReturn(what_you_want).when(spy_objA).method()
Edit: Can find a similar question you may want to check
How to mock another method in the same class which is being tested?

JUnit test Custom exception that throws #httpStatus

I would like to test that my custom exception is thrown when I call a method like that:
private MyObject queryDatabase(String request, String end){
HttpEntity myEntity = new NStringEntity(request, ContentType.APPLICATION_JSON)
Response response = restClient.performRequest("GET", end,Collections.emptyMap(), myEntity)
MyObject myObject = mapper.readValue(response.getEntity().getContent(), MyObject.Class);
if(myObject.getFunctions().isEmpty()){
throw new EmptyResultException("Empty result");
}
return myObject;
}
My EmptyResultException Class :
#ResponseStatus(value=HttpStatus.NO_CONTENT)
public Class EmptyResultException() extends RuntimeException(){
...
}
I'm just starting with JUnit and I have tried :
#Mock MyService myservice;
#Test(expected=EmptyResultException.class)
public void shouldReturnException() {
when(myService.queryDatabase(anyString(),anyString())).thenReturn(new MyObject());
}
It's not working. The behavior I want to try is in private method. It seems I should use Powermock for such cases. However, I have read it is not good to test private method so I would like to change my method to public. Any help please?
EDIT : My full implementation :
I have a Restcontroller :
#RestController
#RequestMapping...
public class MyController {
#Autowired
MyService myService;
#RequestMapping...
public MyObject findObject(#Valid DtoParameters parameters){
return myService.executeQuery(parameters);
}
}
And in myService Class :
#Service
public class MyService{
public MyObject executeQuery(DtoParameters parameters){
return
queryDatabase(parameters.buildRequest(),parameters.buildEnd());
}
}
If your class under test is MyService, it shouldn't have a #Mock.
I don't know your whole implementation, but let's assume MyService have injected dependencies (like your mapper)
First I would mock the mapper and inject it in MyService:
#Mock
private ObjectMapper mapper;
#InjectMocks
private MyService myService;
For this to work, your test class need to use the mockito runner. You can annotate your class with this:
#RunWith(MockitoJUnitRunner.class)
The mocks will be injected into MyService.
Now you can mock your mapper to return an empty object:
when(mapper.readValue(any(),any())).thenReturn(new MyObject());
myService.queryDatabase("request", "end")
when calling queryDatabase, this should call the mock inside.
If you were trying to test your rest controller, it is a different way. What I suggested is a unit test on MyService. If you want to test the controller, you should do it with a SpringRunner, SpringBootTest and MockMvc. But first, unit test your service, and add other tests on your controller later. For the controller, you would annotate MyService with #MockBean and throw, or Autowire it and #MockBean the mapper inside to return an empty object like the unit test.
Check this: https://spring.io/guides/gs/testing-web/
old answer:
You could change your method to protected, assuming your test is in the same package as the class you are testing, you will be able to mock it.
To mock your method, you need to write it like this:
when(myService.queryDatabase(anyString(),anyString())).thenThrow(EmptyResultException.class);
You forgot to use the when method to wrap your call.

#Spy not working when annotated on two lists

This is my PersonSeviceImpl class. Here before saving any person I want to run some validators which implements PersonValidator. And also I autowired PersonStatusChangeValidator to use in a different method.
public class PersonServiceImpl implements PersonService {
#Autowired
PersonDao personDao;
#Autowired
List<PersonStatusChangeValidator> statusChangeValidators;
#Autowired
List<PersonValidator> personValidators;
#Override
public Person save(Person person) {
for(PersonValidator validator: personValidators){
validator.validate(person);
}
personDao.save(person);
return person;
}
}
Here I am writing test to verify whether validators are called or not.
#RunWith(PowerMockRunner.class)
public class PersonServiceImplTest {
#Mock
private PersonDao personDao;
#Mock
private PersonValidator personValidator;
#Mock
private PersonStatusChangeValidator statusChangeValidator;
#Spy
private List<PersonStatusChangeValidator> statusChangeValidators = new ArrayList<>();
#Spy
private List<PersonValidator> personValidators = new ArrayList<>();
#InjectMocks
private PersonServiceImpl personService;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
personValidators.add(personValidator);
statusChangeValidators.add(statusChangeValidator);
}
#Test
public void testCreatePerson() throws Exception {
personService.save(new Person());
verify(personValidator, times(1)).validate(any(Person.class));
}
}
The problem is personValidators, statusChangeValidators are null by the time I'm running test. And when there is a single #Spy annotation in the test, it is working fine. Need some help to know where I'm doing wrong.
Your test doesn't show any mocking of statics or finals so I'm wondering why you are using #RunWith(PowerMockRunner.class)?
I have taken your code and successfully run it with just one change: replacing #RunWith(PowerMockRunner.class) with #RunWith(MockitoJUnitRunner.class).
So, unless there is some other aspect of your tests which require PowerMock then I'd suggest running your test case with MockitoJUnitRunner. If there is some aspect of your tests which require PowerMock then I'd suggest injecting the statusChangeValidators and personValidators instances with explicit constructor injection (i.e. what #JB Nizet suggested in his comment above) so that you do not have to rely on #InjectMocks.
FWIW, there's an open issue against PowerMock for this inability to apply #InjectMocks for fields with the #Spy annotation.

Categories