[英]Mockito when/then not returning expected value
我正在尝试使用“ any”匹配器对这个getKeyFromStream方法进行存根。 我尝试了更明确和不太明确的(anyObject()),但是无论我如何尝试,此存根都不会在我的单元测试中返回fooKey。
我想知道是否是因为它受到保护,或者我缺少其他东西或做错了什么。 在整个测试中,我还有其他when / then语句在起作用,但是由于某种原因,事实并非如此。
注意:getKeyFromStream通常使用byteArrayInputStream,但是我尝试将其与InputStream匹配,但都尝试了无济于事。
public class FooKeyRetriever() //Mocked this guy
{
public FooKey getKey(String keyName) throws KeyException {
return getKeyFromStream(getKeyStream(keyName, false), keyName);
}
//Stubbed this method to return a key object which has been mocked
protected FooKey getKeyFromStream(InputStream keyStream, String keyName){
//Some code
return fooKey;
}
}
单元测试
@Mock
private FooKeyRetriever mockKeyRetriever;
@Mock
private FooKey fooKey;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void testGetFooKey() throws Exception {
when(foo.getKeyFromStream(any(InputStream.class),any(String.class))).thenReturn(fooKey);
FooKey fooKey = mockKeyRetriever.getKey("irrelevant_key");
assertNotNull(fooKey);
}
单元测试的问题在于,您正在尝试模拟要测试的实际类的方法,但实际上无法调用模拟方法,因为除非您在该方法上声明了模拟的返回值,否则它将返回null。调用的方法。 通常,您只模拟外部依赖项。
实际上,有两种创建测试对象的方法: mock
和spy
。 入门者将根据您提供的类创建一个新对象,该类的内部状态为null,并且在每个调用的方法上都返回null
。 这就是为什么您需要为方法调用定义某些返回值的原因。 另一方面,如果为某些方法定义了“模拟定义”,则spy
会创建一个真实的对象并拦截方法调用。
Mockito和PowerMock提供了两种定义模拟方法的方法:
// method 1
when(mockedObject.methodToMock(any(Param1.class), any(Param2.class),...)
.thenReturn(answer);
when(mockedObject, method(Dependency.class, "methodToMock", Parameter1.class, Parameter2.class, ...)
.thenReturn(answer);
要么
// method 2
doReturn(answer).when(mockedObject).methodToMock(param1, param2);
所不同的是, method 1
将执行方法实现,而后一种则不会。 如果您要处理spy
对象,这很重要,因为您有时不想执行所调用方法中的真实代码,而只是替换代码或返回预定义的值!
虽然和的Mockito提供PowerMock一个doCallRealMethod()
您可以定义的,而不是doReturn(...)
或doThrow(...)
这将调用和您的真实对象中执行代码和忽略任何模拟的方法return语句。 但是,在您要模拟被测类的方法的情况下,这并不是很有用。
可以通过以下方法“覆盖”方法实现:
doAnswer(Answer<T>() {
@Override
public T answer(InvocationOnMock invocation) throws Throwable {
...
}
)
您可以在其中简单地声明所调用方法的逻辑。 因此,您可以利用此方法返回受保护方法的模拟结果:
import static org.hamcrest.core.IsSame.sameInstance;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import java.io.InputStream;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class FooKeyRetrieverTest {
@Test
public void testGetFooKey() throws Exception {
// Arrange
final FooKeyRetriever sut = spy(new FooKeyRetriever());
FooKey mockedKey = mock(FooKey.class);
doReturn(mockedKey)
.when(sut).getKeyFromStream(any(InputStream.class), anyString());
doAnswer(new Answer<FooKey>() {
public FooKey answer(InvocationOnMock invocation) throws Throwable {
return sut.getKeyFromStream(null, "");
}
}).when(sut).getKey(anyString());
// Act
FooKey ret = sut.getKey("test");
// Assert
assertThat(ret, sameInstance(mockedKey));
}
}
上面的代码有效,但是请注意,它的语义与简单地声明getKey(...)
的返回值相同getKey(...)
doReturn(mockedKey).when(sut).getKey(anyString());
尝试仅使用getKeyFromStream(...)
方式修改getKeyFromStream(...)
:
doReturn(mockedKey)
.when(sut).getKeyFromStream(any(InputStream.class), anyString());
如果不修改测试中的系统(SUT)的getKey(...)
,将不会实现任何目标,因为将执行getKey(...)
的真实代码。 但是,如果模拟sut对象,则无法在// Act
部分中调用该方法,因为这将返回null。 如果你试试
doCallRealMethod().when(sut).getKey(anyString());
在模拟对象上,将调用真实方法,并且如前所述,这还将调用getKeyFromStream(...)
和getKeyStream(...)
的实际实现,而不管您指定为模拟方法是什么。
正如您可能自己看到的那样,测试中的实际类的模拟方法不是那么有用,并且给您带来的负担超过了它提供的任何好处。 因此,如果您想要或需要完全测试私有/受保护的方法,或者仅坚持测试公共API(我建议这样做),则取决于您或您企业的政策。 尽管重构的主要目的应该是改善代码的总体设计,但是您也可以重构代码以提高可测试性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.