I am new to Mockito, please help in understanding the basic.
According to me above code is supposed to print 5 when mocked.add(6,7) gets called , but add() method is not getting called and the code prints 0.. why ? any solution for this code ?
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
class Calc{
int add(int a,int b){
System.out.println("add method called");
return a+b;
}
}
class MockTest{
public static void main(String[] args) {
Calc mocked=mock(Calc.class);
when(mocked.add(2,3)).thenReturn(5);
System.out.println(mocked.add(6,7));
}
}
In order to get result of 5, you have to pass the exact params as when you set up the when..then. Otherwise mockito will return a 'default' value (which is 0 for integer:
What values do mocks return by default?
In order to be transparent and unobtrusive all Mockito mocks by
default return 'nice' values. For example: zeros, falseys, empty
collections or nulls. Refer to javadocs about stubbing to see exactly
what values are returned by default.
If you want to return 5 for any integer then use:
when(mocked.add(Mockito.any(Integer.class),Mockito.any(Integer.class))).thenReturn(5);
A "mock" is just an empty dummy object that simulates behaviour of a "real" object. If you define a behaviour such like when(mocked.add(2,3)).thenReturn(5); you specifically tell this mock what to do, when it receives those exact values.
mocked.add(6,7) will return 0 at that point, since you haven't defined its behaviour for those values and therefore uses a default value. So if you want to cover all possible inputs, you can go with the solution #MaciejKowalski posted and use the generic matchers like Mockito.any(Integer.class).
Still I believe it is not clear how to correctly handle mocks. Mocks are a way of providing external dependencies to a system-under-test without the need to set up a whole dependency tree. The real methods inside that class are usually not called. That's what something like when(mocked.add(2,3)).thenReturn(5); means. It tells the mock to behave like the real dependency without actually having it at hand.
An example could look like this:
public class TestClass {
private ExternalDependency dep;
public void setDep(ExternalDependency dep) {
this.dep = dep;
}
public int calculate() {
return 5 + dep.doStuff();
}
}
public class ExternalDependency {
public int doStuff() {
return 3;
}
}
Now in your test code you can use mocks like this:
#Test
public void should_use_external_dependency() {
// Aquire a mocked object of the class
ExternalDependency mockedDep = Mockito.mock(ExternalDependency.class);
// Define its behaviour
Mockito.when(mockedDep.doStuff()).thenReturn(20);
TestClass sut = new TestClass();
sut.setDep(mockedDep);
// should return 25, since we've defined the mocks behaviour to return 20
Assert.assertEquals(25, sut.calculate());
}
If sut.calculate() is invoked, the method in ExternalDependency should not be really called, it delegates to the mocked stub object instead. But if you want to call the real method of the real class, you could use a Spy instead with Mockito.spy(ExternalDependency.class) or you could do that with when(mockedDep.doStuff()).thenCallRealMethod();
Related
I'm using Junit5 for test my spring code.
In the test code, I mock SetRepository, to make the getSize() method return value 40. Then I stubbed ListService's findSetSize() method and made it return value 30.
Then print the getSize() method value. Expected 40, but 30 printed.
It looks likegetSize() was overwritten while stubbing, but I'd like to know why.
// ListService.java
#Service
#RequiredArgsConstructor
public class ListService {
private final SetRepository setRepository;
public int findSetSize() {
int size = setRepository.getSize();
System.out.println(size); // first result: 40, second result: 30
return size;
}
public void saveToSet(int num) {
setRepository.saveNum(num);
}
}
// SetRepository.java
#Component
public class SetRepository {
Set<Integer> set = new HashSet<>();
public int getSize() {
return set.size();
}
public void saveNum(int num) {
set.add(num);
}
}
// Test code for ListService
#ExtendWith(MockitoExtension.class)
public class ListServiceWithMockTest {
#InjectMocks
#Spy
ListService subject;
#Mock
SetRepository setRepository;
#Test
#DisplayName("can get Set size")
public void findSetSizeTest() {
//given
when(setRepository.getSize()).thenReturn(40);
when(subject.findSetSize()).thenReturn(30);
//when
subject.saveToSet(100);
//then
System.out.println(setRepository.getSize()); // expected:40, result: 30
assertEquals(30, subject.findSetSize());
}
}
Here is my code. I know I shouldn't do stubbing like this, but I'm curious why the value comes out like this.
Thank U!!
In your example, you want to mock a method within the same class. But this is not possible in the way you did. You create a real object using #InjectMocks. You need to use Mockito.spy() to partially mock this.
If you want to get the results you expect correctly and really mock your findSetSize() method, you should change your test method to follow:
#Test
#DisplayName("can get Set size")
void findSetSizeTest() {
//given
ListService spyService = spy(subject);
when(setRepository.getSize()).thenReturn(40);
doReturn(30).when(spyService).findSetSize();
//when
subject.saveToSet(100);
//then
System.out.println(setRepository.getSize()); // expected:40, result: 30
Assertions.assertEquals(30, spyService.findSetSize());
}
Here you have to understand that mocking would work only when you call it on the instance of spy object. It's wrapped by a Mockito proxy which catches your call, and if you have overriden some method, it will call your new implementation instead of the original one.
Also the documentation related to spy says:
You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).
You can review the answers here and here regarding the differences between when().thenReturn() and doReturn().when().
For example:
public class NumberService {
public boolean isNaturalNumber(int num) {
return num > 0;
}
public String getClassificationInfo(int num) {
return isNaturalNumber(num) ? num + "is a natural number" : num + "is not a natural number";
}
}
Here are two public method, and the method getClassificationInfo called the method isNaturalNumber.
Is it a good practice to mock isNaturalNumber when testing getClassificationInfo?
Like this:
#RunWith(MockitoJUnitRunner.class)
public class NumberServiceTest {
#Spy
private NumberService numberService;
#Test
public void test_getClassificationInfo_when_is_natural_number() {
int num = generateInt();
doReturn(true).when(numberService).isNaturalNumber(num);
String classificationInfo = numberService.getClassificationInfo(num);
assertThat(classificationInfo).isEqualTo(num + "is a natural number");
}
#Test
public void test_getClassificationInfo_when_is_not_natural_number() {
int num = generateInt();
doReturn(false).when(numberService).isNaturalNumber(num);
String classificationInfo = numberService.getClassificationInfo(num);
assertThat(classificationInfo).isEqualTo(num + "is not a natural number");
}
// the other test for isNaturalNumber
private int generateInt() {
return new Random().nextInt();
}
}
In this particular case it does not make sense to mock the method called. It is, however, not just because of the fact that it is a method of the same class. In fact, sometimes methods of the same class are introduced specifically to allow them to be mocked. For example, if within your class you process some data you obtain via file I/O, you could isolate the file I/O into a method of its own. Then, you could test the data processing with a mocked version of your file I/O method.
However, in this case there is no good reason to mock isNaturalNumber in the tests for getClassificationInfo: The method isNaturalNumber has deterministic behaviour and does not lead to unacceptably long execution time. It also seems unlikely that you gain much by simulating the case that isNaturalNumber intentionally behaves wrongly in one of the tests of getClassificationInfo.
You don't have to mock everything dogmatically. For example, you also don't mock standard library math functions like sin or cos, because they also don't cause problems in most cases. Mocking should be done for a reason. Therefore, if you consider mocking a function or method, you should know which problem you are about to solve. If there is no problem to solve, don't mock.
No Its not good practice because we should mock stubs where other module/class object is involve.
eg:
public class A{
private B aObj;
public void aDoingSomeBWork(){
aObj.someMethod();
someLogic();
}
private/public/any modifier void someLogic(){
//some logic
}
}
public class B{
public void someMethod(){}
}
then in that you should mock B's someMethod() but should not A's someLogic() beacuse you are unit testing A's aDoingSomeBWork() method. if you mock someLogic(); in unit testing then there might be a case your code break.
I have started learning JUNIT.
Here is what i am trying to achieve.
I have a class which checks if the inputString is part of secretKey;
public class StringChecker {
public boolean isEqual(String name)
{
boolean isEqual = false;
if(getSecretKey().contains(name))
{
isEqual = true;
}
return isEqual;
}
public String getSecretKey()
{
return "OSKAR";
}
}
My test class is this
public class RandomCheck {
#Test
public void isEqualTest()
{
StringChecker stringChecker = mock(StringChecker.class);
when(stringChecker.getSecretKey()).thenReturn("james");
//assertEquals(true, new StringChecker().isEqual("OSKAR")); <----this test case passes
assertEquals(true, stringChecker.isEqual("james"));
}
}
When i use Mocked object it does not give me the expected result, hence failing the test. But when i use a real object it gives me expected result and passes the test.
Am i missing anything? Like any annotation
A mockito mock is an object having the interface of the mocked class, but not its implementation. Your StringChecker is mocked, meaning there is no implementation code making calls from isEqual to getSecretKey as you assume.
You could use mockito spy, See this SO question:
Mockito.spy() is a recommended way of creating partial mocks. The reason is it guarantees real methods are called against correctly constructed object because you're responsible for constructing the object passed to spy() method.
ROOKIE MISTAKE
Here's the rookie mistake i did (mentioned by Arnold).
I mocked the StringChecker class but i did not provide any implementation for isEqual(String) method.
And because there was no implementation, i was getting the default value. In this case false (return type of method is boolean).
Solution
Using static method spy(). (Again mentioned by #Arnold).
So here is what my working code looks like.
#Test
public void isEqualTest()
{
StringChecker stringChecker = new StringChecker();
StringChecker spy = spy(stringChecker);
when(spy.getSecretKey()).thenReturn("james"); // providing implementation for the method
assertEquals(true, spy.isEqual("james"));
}
What i learnt from it.
Just by mocking an object does not get your things done if you intend to use methods of mocked object (In simple terms PROVIDE IMPLEMENTATION for methods of mocked objects).
TIP
If you want to see the default value returned by mocked object, just call the method of mocked object in sysout(without giving implementation).
Hope it will help someone like me.Peace
An alternative way without mocking and with additional test cases:
#Test
public void isEqualTest() {
StringChecker stringChecker = new StringChecker() {
#Override
public String getSecretKey() {
return "james";
}
};
assertTrue(stringChacker.isEqual("james"));
assertTrue(stringChacker.isEqual("jam"));
assertTrue(stringChacker.isEqual("mes"));
assertFalse(stringChacker.isEqual("oops"));
}
BTW, the isEqual() can be simplified to one line:
public boolean isEqual(String name) {
return getSecretKey().contains(name);
}
Suppose I have the following class :
public class Math {
public int mult(int a, int b) {
return 4;
}
public int mul (int a, int b) {
return mult(a,b);
}
}
And the following test class :
public class TestMockito {
Math testMath;
#Before
public void create () {
testMath = *mock*(Math.class);
when(testMath.mult(1,2).thenReturn(2);
}
#Test
public void test() {
System.out.println(testMath.mul(1,2));
}
}
Why does mul(1,2) called in test() not use when(testMath.mult(1,2).thenReturn(2); ?
Is there any other way to mock a method being used inside another method that is being tested ?
Cheers
You usually do not mock the code under test (unless it is an abstract class).
You usually mock other classes (the dependencies) your CUT communicates with.
The reason why your test does not work (as you expect) is that the mock is not an object of the real class (which is the reason why we mock it BTW....). It has been derived by the mocking framework not to behave like the original code but like it has been configured for the test.
If you really want the real methods being called in the mock (which is not what you want most of the time) you need to tell mockito that when creating the mock:
mock(ClassToBeMocked.class,Mockito.CALL_REAL_METHODS);
I need to test if one objects calls other object methods in right order.
Unfortunately it looks like Mockito use some kind of grouping for calls with the same parameters. I prepared example which illustrates this behavior.
public class TestInOrder {
#Test
public void test() {
Foo foo = new Foo();
Bar mockBar = mock(Bar.class);
foo.run(mockBar);
InOrder order = Mockito.inOrder(mockBar);
order.verify(mockBar).A();
order.verify(mockBar).B();
order.verify(mockBar).B();
order.verify(mockBar).A();
order.verify(mockBar).B();
}
}
class Foo {
void run(Bar mockBar) {
mockBar.A();
mockBar.B();
mockBar.B();
mockBar.A();
mockBar.B();
}
}
class Bar {
public void A() {
}
public void B() {
}
}
The result is:
org.mockito.exceptions.verification.VerificationInOrderFailure:
Verification in order failure:
bar.B();
Wanted 1 time:
-> at com.goeuro.pi.provider.busfor.TestInOrder.test(TestInOrder.java:19)
But was 3 times. Undesired invocation:
-> at com.goeuro.pi.provider.busfor.Foo.run(TestInOrder.java:32)
I don't understand why I get this error. Order of calls in test is the same as in method.
The issue is that you expect one invocation on each mock. Instead Mockito will count all the invocation on certain mock and will fail when 2 in a row will happen.
This is the implementation for verify method in InOrderImpl class.
public <T> T verify(T mock) {
return this.verify(mock, VerificationModeFactory.times(1));
}
As you can see, it tells mockito to expect exact one invocation on method.
From JavaDoc:
Allows verifying exact number of invocations.
Use VerificationMode to tell Mockito how to verify you mock. This should help:
InOrder order = Mockito.inOrder(mockBar);
order.verify(mockBar).A();
order.verify(mockBar, times(2)).B();
order.verify(mockBar).A();
order.verify(mockBar).B();