Mockito当/然后没有返回预期值

时间:2015-07-14 17:22:53

标签: java unit-testing mockito stubbing

我试图使用' any'来存储此getKeyFromStream方法。匹配器。我尝试过更明确,更不明确(anyObject()),但似乎无论我尝试什么,这个存根都不会在我的单元测试中返回fooKey。

我想知道这是因为它受到保护还是还有其他我遗失或做错的事情。我在整个测试过程中有其他的/ em语句正在工作但是出于某种原因,它不是。

注意:getKeyFromStream通常使用byteArrayInputStream,但是我试图将它与Inp​​utStream匹配,我试过两者都无济于事。

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);
}

1 个答案:

答案 0 :(得分:8)

您的单元测试的问题是,您正在尝试模拟您要测试的实际类的方法,但您实际上无法调用模拟方法,因为这将返回null,除非您声明对该调用方法的模拟返回值。通常,您只模拟外部依赖项。

实际上有两种方法可以创建测试对象:mockspy。引导程序将根据您提供的类创建一个新对象,该类具有内部状态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(...),但这将调用并执行真实对象中的代码并忽略任何模拟的方法返回语句。但是,在您想要模拟测试类的方法的情况下,这不是很有用。

方法实现可以被覆盖"通过

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());

未修改您的被测系统(SUT)的getKey(...)无法实现任何目标,因为getKey(...)的实际代码将被执行。但是,如果您模拟了sut-object,则无法在// Act部分中调用该方法,因为这将返回null。如果你试试

doCallRealMethod().when(sut).getKey(anyString());

在模拟对象上,真正的方法可以被调用,并且如前所述,这也会调用getKeyFromStream(...)getKeyStream(...)的实际实现,无论你指定为mock-method。

正如你自己可能看到的那样,你所测试的实际课程的模拟方法并没有那么有用,给你带来的负担比提供任何好处更多。因此,它取决于您或您的企业&#39;策略,如果您想要或者需要测试私有/受保护的方法,或者您坚持只测试公共API(我建议)。您还可以refactor your code来提高可测试性,尽管重构的主要目的应该是improve the overall design of your code