java Mockito 接口的模拟 getter/setter 方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26421021/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mocking getter / setter method of Interface with Mockito
提问by Paul
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
我正在尝试使用 jUnit 和 Mockito 测试我的实现,但遇到了问题。这是一个非常简单的例子,它解释了这个问题
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
类 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
jUnit 测试类
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).
我的问题是,出于我无法控制的技术原因,我需要模拟 KeyValue。因此,我不能只使用 testNotMocked() 方法。同样出于我无法控制的技术原因,我必须模拟界面(而不是类)。
Is there any way to achieve this?
有没有办法实现这一目标?
Thanks a lot.
非常感谢。
采纳答案by JB Nizet
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:
如果您要编写您正在测试的方法的 javadoc,甚至不知道接口的任何方法在做什么,您将编写以下内容:
/**
* 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
你甚至不应该假设getValue()
返回之前设置的值。这当然不是模拟会做的事情,因为模拟除了你告诉它做的事情之外不会做任何事情。你应该做的就是测试你的方法的契约,而不用假设任何关于接口的实现。所以你的测试应该是
@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);
}
回答by kan
Mock does not know anything about your Impl class. So, just either do verify
for setValue
or use spy to callreal methods.
Mock 对您的 Impl 类一无所知。所以,要么使用verify
forsetValue
要么使用 spy 来调用真正的方法。
回答by vikeng21
if you check the Mockito API for the mock method below you can see that it creates mock object of given class or interface.
如果您检查下面的模拟方法的 Mockito API,您可以看到它创建了给定类或接口的模拟对象。
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
所以第一个方法 testMocked1() 的错误是有效的。你实际上在做什么是间接地嘲笑该接口的 impl。因此,当您这样做时,所有方法都会被模拟,并且由于 getValue() 返回一个 String,因此 String 的默认值为 null,因此返回 NULL。使用如下所示的 ReflectionUtils 直接设置键值
ReflectionTestUtils.setField(classObject, key,keyvalue);
and do below in your method testMocked1()
并在您的方法 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
同样,对于第二个方法 testMocked2() 使用反射工具设置值并使用 Mockito 中的任何 api 方法来做同样的事情
回答by Grzesuav
Use when/then to configure your mock. See http://www.baeldung.com/mockito-behavior
使用 when/then 来配置你的模拟。见http://www.baeldung.com/mockito-behavior