Reputation: 419
I am trying to test my implementation with jUnit and Mockito and I am running into problems. Here is a very simplified example which explains the issue
Interface KeyValueInterface
public interface KeyValueInterface {
public abstract String getKey();
public abstract void setKey(String key);
public abstract String getValue();
public abstract void setValue(String value);
}
Class KeyValueImpl
public class KeyValueImpl implements KeyValueInterface {
private String key;
private String value;
@Override
public String getKey() {
return key;
}
@Override
public void setKey(String key) {
this.key = key;
}
@Override
public String getValue() {
return value;
}
@Override
public void setValue(String value) {
this.value = value;
}
}
Class with "business logic"
public class ValueFinder {
public KeyValueInterface findValueForKey(KeyValueInterface keyValue){
keyValue.setValue("foo");
return keyValue;
}
}
jUnit Test class
import static org.junit.Assert.*;
import org.junit.Test;
import org.mockito.Mockito;
public class ValueFinderTest {
@Test
public void testNotMocked() {
KeyValueInterface keyValue = new KeyValueImpl();
keyValue = (new ValueFinder()).findValueForKey(keyValue);
assertEquals("foo", keyValue.getValue()); // works fine
}
@Test
public void testMocked1() {
KeyValueInterface keyValue = Mockito.mock(KeyValueInterface.class);
keyValue = (new ValueFinder()).findValueForKey(keyValue);
assertEquals("foo", keyValue.getValue()); // java.lang.AssertionError:
// expected:<foo> but
// was:<null>
}
@Test
public void testMocked2() {
KeyValueInterface keyValue = Mockito.mock(KeyValueInterface.class);
keyValue = (new ValueFinder()).findValueForKey(keyValue);
Mockito.when(keyValue.getValue()).thenCallRealMethod();
Mockito.doCallRealMethod().when(keyValue).setValue(Mockito.any(String.class));
assertEquals("foo", keyValue.getValue()); // org.mockito.exceptions.base.MockitoException:
// Cannot call real method
// on java interface.
// Interface does not have
// any implementation!
// Calling real methods is
// only possible when
// mocking concrete classes.
}
}
My probelm is, that I need to mock KeyValue for technical reasons which are beyond my control. Therefore I cannot just go with method testNotMocked(). Also for technical reasons beyond my control I have to mock the interface (and not the class).
Is there any way to achieve this?
Thanks a lot.
Upvotes: 3
Views: 20648
Reputation: 479
Use when/then to configure your mock. See http://www.baeldung.com/mockito-behavior
Upvotes: 0
Reputation: 531
if you check the Mockito API for the mock method below you can see that it creates mock object of given class or interface.
public static <T> T mock(java.lang.Class<T> classToMock)
So the error for the first method testMocked1() is a valid one. what you are actually doing there is mocking the impl for that interface indirectly. So when you do that all the methods get mocked and since getValue() returns a String so the default value of String is null so a NULL is getting returned. Use ReflectionUtils like below to set key value directly
ReflectionTestUtils.setField(classObject, key,keyvalue);
and do below in your method testMocked1()
assertEquals("foo", keyValue.getValue());
Similarly for the 2nd method testMocked2() do the same by using reflectionutils to set the value and use any of the api methods from Mockito
Upvotes: 0
Reputation: 692181
If you were to write the javadoc of the method you're testing, without even knowing what any of the methods of the interface are doing, you would write the following:
/**
* Sets "foo" as the value of the given keyValue, and returns it
*/
You shouldn't even assume that getValue()
returns the value that has been set before. This is certainly not what the mock will do, since the mock doesn't do anything other than what you tell it to do. All you should do is test the contract of your method, without assuming anything about the implementation of the interface. So your test should be
@Test
public void testMocked1() {
KeyValueInterface keyValue = Mockito.mock(KeyValueInterface.class);
KeyValueInterface result = (new ValueFinder()).findValueForKey(keyValue);
// tests that the value has been set to "foo"
verify(keyValue).setValue("foo");
// tests that the method returns its argument
assertSame(keyValue, result);
}
Upvotes: 3
Reputation: 28981
Mock does not know anything about your Impl class. So, just either do verify
for setValue
or use spy to callreal methods.
Upvotes: 1