我正在尝试使用“ 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(...)
or doThrow(...)
,但是它将在您的真实对象中调用并执行代码,并且忽略任何模拟的方法返回语句。但是,在您要模拟被测类的方法的情况下,这并不是很有用。
可以通过以下方法“覆盖”方法实现:
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(...)
作为
doReturn(mockedKey).when(sut).getKey(anyString());
尝试仅getKeyFromStream(...)
使用以下内容进行修改:
doReturn(mockedKey)
.when(sut).getKeyFromStream(any(InputStream.class), anyString());
如果不修改getKey(...)
系统测试中的内容(SUT),将不会实现任何效果,因为getKey(...)
将执行真正的代码。但是,如果模拟sut对象,则无法调用本// Act
节中的方法,因为这将返回null。如果你试试
doCallRealMethod().when(sut).getKey(anyString());
在模拟对象上,将调用真实方法,并且如前所述,这还将调用的实际实现,getKeyFromStream(...)
而getKeyStream(...)
无论您指定为模拟方法是什么。
正如您可能自己看到的那样,测试中的实际类的模拟方法不是那么有用,并且给您带来的负担超过了它提供的任何好处。因此,如果您想要或需要完全测试私有/受保护的方法,或者仅坚持测试公共API(我建议这样做),则取决于您或您企业的政策。尽管重构的主要目的应该是改善代码的整体设计,但是您也可以重构代码以提高可测试性。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句