Mockito Spy behaves like Mock - mockito

Environment: Java11, AWS Lambda, Dagger, JUnit5 (5.9.1), Mockito(4.8.1) (tried both mockito core & inline)
I have classes A, B and C.
C is injected to B and B is injected to A.
Method A.ma1() calls B.mb1() and B.mb1 calls C.mc1()
I just want to verify if a method "mc1" of C is called or not if I call A.ma1
Here is my structure:
#ExtendWith(MockitoExtension.class)
class Test {
#InjectMocks
A a;
#Spy
B b;
#Mock
C c;
#Test
public void test() {
a.ma1();
verify(c).mc1(any());
}
}
The problem is when A.ma1() calls B.mb1(), B.mb1() behaves like a Mock instead of a Spy.
The contents of the method never being executed during debugging, but immediately returning. I also tried to annotate B with #InjectMocks along with #Spy. It also did not work, throwing a different kind of exception.
Does anybody have any idea why does B behave like a Mock instead of a Spy?
Added after comment:
Here is the complete code: (simplified)
AService is A, InternalService is B and PanelDao is C in the above example. B's "selectMappedInternal" never executes line by line but returns immediately like a mock when called.
#ExtendWith(MockitoExtension.class)
class Test {
#InjectMocks
AService aService ;
#Spy
InternalService internalService ;
#Mock
PanelDao panelDao;
#Test
public void test() {
..
}
}
public class AService {
#Inject
InternalService internalService;
#Inject
public AService() {
}
public void processMessage(AccSqsMessage accSqsMessage) {
..
internalSet = internalService.selectMappedInternal(accSqsMessage.getCode());
..
}
}
public class InternalServiceImpl implements InternalService {
#Inject
PanelDao panelDao;
#Inject
public InternalServiceImpl() {
}
#Override
public Set<Internal> selectMappedInternal(String code) {
..
Optional<Panel> p = panelDao.findPanelByCode(code);
..
}
}

Finally able to solve it as below.
My guess about cause of the problem is implementation class "InternalServiceImpl" for interface "InternalService" could not be injected properly to unit test class (despite it is working fine when injected to a business class), so during debugging, I was not able to see execution of methods in spy object.
After spying (without annonation) an explicitly provided implementation class, everything seems solved.
#ExtendWith(MockitoExtension.class)
class Test {
#InjectMocks
AService aService ;
#InjectMocks
InternalService internalService = Mockito.spy(InternalServiceImpl.class);
#Mock
PanelDao panelDao;
#Test
public void test() {
..
}
}

Related

Mockito: How to test a class's void method?

Unit test noob here.
I have three classes: Db1Dao, Db2Dao, ExecuteClass where Db1Dao, Db2Dao are database access objects for two different databases. My goal is to fetch some data from db1 using Db1Dao and run executeClass.execute() to "put" the processed data into db2 using Db2Dao.
My ExecuteClass looks like this:
class ExecuteClass {
private Db1Dao db1Dao;
private Db2Dao db2Dao;
public void execute() {
...
List<String> listOfString = getExternalData(someParam);
List<Metadata> metadatum = db1Dao.get(someInputs);
... I do something to generate a list of new class `A` based on listOfString & metadatum ...
try {
db2Dao.put(listOfA);
} catch (PutException e){
...
}
}
public List<String> getExternalData(SomeClass someParam){
... do something
return listOfString;
}
}
Now I want to test:
Given a specific listOfString (returned by getExternalData) and a specific metadatum (returned by db1Dao.get):
Will I get the desired listOfA?
Am I able to call db2Dao.put and its input parameter is listOfA?
Particularly, I have hard-coded sample listOfString and metadatum and desired listOfA (and they will be passed via an object MockData, see the following code) but I don't know how to write the test using Mockito. The following is a test class I wrote but it does not work:
class TestClass extends BaseTest {
#Mock
private Db1Dao db1Dao;
#Mock
private Db2Dao db2Dao;
private ExecuteClass executeClass;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
executeClass = new ExecuteClass(db1Dao, db2Dao);
}
#ParameterizedTest
#MethodSource("MockDataProvider")
public void executeClassTest(final MockData mockData) throws PutException {
Mockito.when(db1Dao.get(Mockito.any(), ...))
.thenReturn(mockData.getMetadatum());
ExecuteClass executeClassSpy = Mockito.spy(executeClass);
Mockito.when(executeClassSpy.getExternalData(Mockito.any()))
.thenReturn(mockData.getListOfString());
executeClassSpy.execute();
// executeClass.execute(); not working neither...
List<A> listOfA = mockData.getDesiredListOfA();
Mockito.verify(db2Dao).put(listOfA);
}
}
Could anyone please let me know? Thank you in advance!!
You should not create a spy of the same class you want to test. Instead, try to write a unit test for the smallest amount of code (e.g. a public method) and mock every external operator (in your case Db1Dao and Db2Dao).
If testing a public method involves calling another public method of the same class, make sure to mock everything inside the other public method (in your case getExternalData). Otherwise, this other public method might be a good candidate for an extra class to have clear separation of concerns.
So, remove the ExecuteClass executeClassSpy = Mockito.spy(executeClass); and make sure you setup everything with Mockito what's called within getExternalData.
To now actually, verify that Db2Dao was called with the correct parameter, either use your current approach with verifying the payload. But here it's important to 100% create the same data structure you get while executing your application code.
Another solution would be to use Mockito's #Captor. This allows you to capture the value of why verifying the invocation of a mock. Later on, you can also write assertions on the captured value:
#Captor
private ArgumentCaptor<ClassOfListOfA> argumentCaptor;
#Test
public void yourTest() {
Mockito.verify(db2Dao).put(argumentCaptor.capture());
assertEquals("StringValue", argumentCaptur.getValue().getWhateverGetterYouHave);
}
The following code worked for me.
I partially accepted #rieckpil's answer. I used #Captor which is very handy.
The reason I had to mock getExternalData() is because its implementation is still a "TODO".
class TestClass extends BaseTest {
#Mock
private Db1Dao db1Dao;
#Mock
private Db2Dao db2Dao;
#Captor
private ArgumentCaptor<List<A>> argumentCaptor;
private ExecuteClass executeClass;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
executeClass = new ExecuteClass(db1Dao, db2Dao);
}
#ParameterizedTest
#MethodSource("MockDataProvider")
public void executeClassTest(final MockData mockData) throws PutException {
Mockito.when(db1Dao.get(Mockito.any(), ...))
.thenReturn(mockData.getMetadatum());
ExecuteClass executeClassSpy = Mockito.spy(executeClass);
Mockito.when(executeClassSpy.getExternalData(Mockito.any()))
.thenReturn(mockData.getListOfString());
executeClassSpy.execute();
List<A> listOfA = mockData.getDesiredListOfA();
Mockito.verify(db2Dao).put(argumentCaptor.capture());
assertEquals(listOfA, argumentCaptor.getValue());
}
}

Call #Autowire bean through Mockito

I have Class A and Class B. B is autowired in class A. Now I want to test the flow using mockito.
So the problem is when I tried to mock the class A and B in my test case using #InjectMock its going to class A but its not invoking class B.
I dont want to mock the class B which is autowired in class A, from A its should make call to class B and get the user details data.
#Component
public class A {
#Autowired
private B b;
public Users getUsers() {
Long id = 10;
b.getUserDetails(id);
// some Logic
}
}
#Component
public class B {
public UserDetails getUserDetails(Long id) {
// some logic to get users details ..
}
}
#RunWith(MockitoJUnitRunner.class)
public class TestA {
#InjectMocks
private A a;
#InjectMocks
private B b;
#Test
public void testA() {
Users actual = a.getUsers();
assertEquals(actual, expected());
assertNotNull(actual);
}
private Users expected() {
return new Users(); // expected users object
}
}
You should use #Spy on B in order to use real B class
#Spy
private B b;
the spy will wrap an existing instance. It will still behave in the same way as the normal instance – the only difference is that it will also be instrumented to track all the interactions with it.
You should change #InjectMocks annotation on above B to #Spy and you should add #Spy on above A also. Because you want to use B.class's and A.class's real methods. Why you need to use #Spy ?
If you use #Mock, by default for all methods, mock returns null, an empty collection or appropriate primitive / primitive wrapper value (e.g. 0, false, null, ...)
If you use #Spy then the real methods are called (unless a method was stubbed).
As a result, your creation in TestA.class should be like :
#Spy #InjectMocks private A a;
#Spy private B b;

Mockito Method calls

I have a DAO implementation class :
public class DataPollingDAOImpl implements DataPollingDAO {
public List<String> getInfo(String id,String columnName)
{
// some code which calls the database and retrieves data.
}
}
I have written a mockito test case as follows-
public class connection{
#Mock private DataPollingDAOImpl myDao;
#Test public void test() {
when(myDao.getInfo("520", "Hole"));
}
}
I created a mock database connection as well. However what shall I do to print success on the console to show a success case after calling myDao.getInfo() in test method?
If you are mocking using #Mock then I suppose you want your mock to return a value, you specify how to return the value by:
when(myDao.getInfo(anyString(), anyString()).thenReturn(Arrays.asList("yourReturnValue"));
If you want to see if you have called your Mock, then normally #Spy is used and you write:
Mockito.verify(myDao).getInfo(anyString(),anyString());
And I do recommend you to use interfaces (as DataPollingDAO) and not implementations (DataPollingDAOImpl)

How to mock System.getenv() in JUnit [Powermock + Parameterized]

How to mock "System.getenv("...")" in JUnit.
Currently I am doing:
#RunWith(Parameterized.class)
#PowerMockRunnerDelegate(PowerMockRunner.class)
#PrepareForTest(System.class)
public class TestClass extends BaseTest {
public TestClass(String testCase) {
this.testCase = testCase;
}
#Before
#Override
public final void initTable() throws Throwable {
super.initTable();
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");
}
...
}
I am using both PowerMock and Parameterizedrunner.
I am getting below exception for line:
PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");
Exception:
org.mockito.exceptions.base.MockitoException:
'afterPropertiesSet' is a *void method* and it *cannot* be stubbed with a *return value*!
Voids are usually stubbed with Throwables:
doThrow(exception).when(mock).someVoidMethod();
***
Use the #RunWith(PowerMockRunner.class) annotation at the class-level of the test case.
Use the #PrepareForTest({ClassThatCallsTheSystemClass.class}) annotation at the class-level of the test case.
Example with using EasyMock
public class SystemClassUser {
public String performEncode() throws UnsupportedEncodingException {
return URLEncoder.encode("string", "enc");
}
}
And test
#RunWith(PowerMockRunner.class)
#PrepareForTest( { SystemClassUser.class })
public class SystemClassUserTest {
#Test
public void assertThatMockingOfNonFinalSystemClassesWorks() throws Exception {
mockStatic(URLEncoder.class);
expect(URLEncoder.encode("string", "enc")).andReturn("something");
replayAll();
assertEquals("something", new SystemClassUser().performEncode());
verifyAll();
}
}
From:
https://github.com/powermock/powermock/wiki/MockSystem
So, you should add a class that uses the System.getenv, not the System class to #PrepareForTest.
This post explains why it should be done in such way.
Also, I'd like to recommend to use the System Rules library for your case. It has a good way to stub environment variables. PowerMock modifies a class byte code, so it makes test slowly. And even if it not modify a class it at least read class from disk.

Mocking two objects of the same type with Mockito

I'm writing unit tests using Mockito and I'm having problems mocking the injected classes. The problem is that two of the injected classes are the same type, and only differentiated by their #Qualifier annotation. If I tried to simply mock SomeClass.class, that mock is not injected and that object is null in my tests. How can I mock these objects?
public class ProfileDAL {
#Inject
#Qualifier("qualifierA")
private SomeClass someClassA ;
#Inject
#Qualifier("qualifierB")
private SomeClass someClassB ;
//...various code, not important
}
#RunWith(MockitoJUnitRunner.class)
public class ProfileDALLOMImplTest {
#InjectMocks
private ProfileDALLOMImpl profileDALLOMImpl = new ProfileDALLOMImpl();
#Mock
private SomeClass someClassA;
#Mock
private SomeClass someClassB;
private SomeResult mockSomeResult = mock(SomeResult.class);
#Test
public void testSomeMethod() {
when(someClassA .getSomething(any(SomeArgment.class)).thenReturn(mockSomeResult);
Int result = profileDALLOMImpl.someTest(This isn't relevant);
}
}
I have tried mocking two objects with the same type with Mockito 1.9.5 using JUnit and it works.
See: http://static.javadoc.io/org.mockito/mockito-core/1.9.5/org/mockito/InjectMocks.html
Relevant type info from the doc:
"Field injection; mocks will first be resolved by type, then, if there is several property of the same type, by the match of the field name and the mock name."
And this one which seems to say you should make the mock name match the field name for all your mocks when you have two of the same type:
"Note 1: If you have fields with the same type (or same erasure), it's better to name all #Mock annotated fields with the matching fields, otherwise Mockito might get confused and injection won't happen."
Perhaps this latter one is biting you?
Just confirmed what Splonk pointed out and it works that way in Mockito 1.9.5, as soon as I removed one of the mocked classes, it failed.
So, in your case, make sure you have both of the mocked classes with the same name as in the class in your test:
#Mock
private SomeClass someClassA;
#Mock
private SomeClass someClassB;
If you don't use annotation, you get something like
public class MyClass {
private MyDependency myDependency;
public void setMyDependency(MyDependency myDependency){
this.myDependency = myDependency;
}
}
and
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.*;
public class MyTest {
private MyClass myClass;
private MyDependency myDependency;
#Before
public void setUp(){
myClass = new MyClass();
myDependency = mock(MyDependency.class);
myClass.setMyDependency(myDependency);
}
#Test
public void test(){
// Given
// When
// Then
}
}
You can do just the same if your object has its dependencies specified via constructor rather than via setter. I guess your dependency injection framework can annotate the setters the same way you annotate private fields, but now your tests don't rely on any dependency injection framework.

Resources