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().
Related
I am testing a class that has a testable method (the method itself below) with a return value.
Using Mockito I am having a problem. Problem with void method roomDao.updateData(outData);
public IEntity getData(SimBasket<DataEntity, SimRequest> request) {
Entity outData = converterData.convertNetToDatabase(request);
roomDao.updateData(outData);
return outData;
}
Here is my test code:
#Test
public void getData() {
simRepo = new SimRepo();
Mockito.when(simRepo.getData(request)).thenReturn(new Entity());
}
Error log:
org.mockito.exceptions.misusing.CannotStubVoidMethodWithReturnValue:
'updateData' is a void method and it cannot be stubbed with a return value!
Voids are usually stubbed with Throwables:
doThrow(exception).when(mock).someVoidMethod();
I don't seem to really understand how to fix this, since the void method is inside a method with a return value.
instead of new SimRepo() try mock it by using Mockito's mock method:
#Test
public void getData() {
SimRepo simRepo =Mockito.mock(SimRepo.class);
Mockito.when(simRepo.getData(request)).thenReturn(new Entity());
}
Update:
If you also want to count the number of times this mock method called use this:
// this will check if mock method getData() with parameter `request` called exactly 1 times or not.
Mockito.verify(simRepo, Mockito.times(1)).getData(request);
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);
}
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();
I have encountered what I assume might be a bug with Mockito, but was wondering if anyone else can shed light as to why this test doesn't work.
Basically, I have two objects, like this:
public class FirstObject {
private SecondObject secondObject;
public SecondObject getSecondObject() { return secondObject; }
}
public class SecondObject {
private String name;
public String getName() { return name; }
}
The first object is mocked via annotation and the before method:
#Mock
FirstObject mockedFirstObject;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
The second object is mocked in a method:
public SecondObject setupMockedSecondObject() {
SecondObject secondObject = Mockito.mock(SecondObject.class);
Mockito.when(secondObject.getName()).thenReturn("MockObject");
return secondObject;
}
When thenReturn contains a direct call to this method to setup and obtain a mock of the second object, it fails:
#Test
public void notWorkingTest() {
Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(setupMockedSecondObject());
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
But, when the mock returned by the same method is assigned to a local variable, which is used in thenReturn, it works:
#Test
public void workingTest() {
SecondObject mockedSecondObject = setupMockedSecondObject();
Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(mockedSecondObject);
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
Are we doing something wrong or is this indeed a bug/limitation in Mockito? Is there a deliberate reason for this not working?
This is indeed a limitation of Mockito, and it is referenced in their FAQ:
Can I thenReturn() an inlined mock()?
Unfortunately you cannot do this:
when(m.foo()).thenReturn(mock(Foo.class));
// ^
The reason is that detecting unfinished stubbing wouldn't work if we allow above construct. We consider is as a 'trade off' of framework validation (see also previous FAQ entry). However you can slightly change the code to make it working:
//extract local variable and start smiling:
Foo foo = mock(Foo.class);
when(m.foo()).thenReturn(foo);
The workaround, as mentioned, is to store the desired returned value in a local variable, like you have done.
The way I understand it is that Mockito validates the usage you make of it every time you call its methods. When another method is called during an on-going stubbing process, you are breaking its validation process.
You can't use a method in thenReturn, but you can in thenAnswer
Your code will be called after the when condition will occur,
unlike any workaround based on thenReturn
Thus you could write:
#Test
public void nowWorkingTest() {
Mockito.when(mockedFirstObject.getSecondObject()).thenAnswer(new Answer<Map>() {
#Override
public Map answer(InvocationOnMock invocation) {
return setupMockedSecondObject();
}
});
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
Let find another example here
#Test
public void testAuthenticate_ValidCredentials() throws FailedToAuthenticateException {
String username = "User1";
String password = "Password";
/*Configure Returning True with when...thenReturn configuration on mock Object - Q5*/
//Write your code here
assertTrue(authenticator.authenticateUser(username, password));
}
I have a problem with a class that I am testing where in almost every method I want to test, one of the first things each method does is call this one specific method. This method that is called once by all of these other methods takes a long time to execute. I don't want to have to wait for this every time I run my tests, it really is just a huge waste of time.
I attempted to #Spy the method using Mocktio, but I ran into problems because the really long method doesn't return anything. Can someone suggest a good way to mock out a single method inside a class I am trying to test?
Example:
public class myClass {
public void methodOne() {
reallyLongMethod();
// More code
}
public void methodTwo() {
reallyLongMethod();
// More code
}
.
.
.
public void methodN() {
reallyLongMethod();
// More code
}
public void reallyLongMethod() {
}
}
This is the class I am trying to test. I want to test all of the 'methodX()' methods. I don't want to run reallyLongMethod everysingle time however.
So, is there a way to use Mockito 'Spy' to stub out reallyLongMethod()? Even though it doesn'treturn anything?
You can use a Spy with doNothing(), but make sure you use the spy during the test. Mockito spies copy the original, instead of delegating to it.
#RunWith(MockitoJUnitRunner.class)
public class YourTest {
// Option 1:
// #Spy MyClass myClass = new MyClass();
// Option 2 (see #Before method)
MyClass myClass;
#Before public void ignoreReallyLongMethod() {
myClass = spy(new MyClass()); // discard original
doNothing().when(myClass).reallyLongMethod();
}
#Test public void methodOneWorks() {
myClass.methodOne(); // you're using the spy here
assertEquals(42, myClass.getFoo());
}
}
Though this does evoke a code smell, don't mock or stub the class under test, as long as you're careful to test the method behavior (methodOne) and not the stubbed behavior (reallyLongMethod) you'll be good to go. If you do want to test reallyLongMethod you'll want to use a different object instance or else you'll "test" the doNothing() call alone. Do bear in mind that if reallyLongMethod and your other methods have any negative interactions, these tests won't tell you about that.
By the way, you can also do the equivalent without using Mockito, which may make a little clearer what you are or aren't doing with your mocks:
#RunWith(JUnit4.class)
public class YourTest {
MyClass myClass;
#Before public void createMyClass() {
myClass = new MyClass() { // create an anonymous inner class
#Override public void reallyLongMethod() {} // that does nothing here
};
}
}