简体   繁体   English

Mockito何时/然后不返回期望值

[英]Mockito when/then not returning expected value

I'm trying to stub this getKeyFromStream method, using the 'any' matchers. 我正在尝试使用“ any”匹配器对这个getKeyFromStream方法进行存根。 I've tried being more explicit and less explicit (anyObject()), but it seems like no matter what I try, this stub will not return the fooKey in my unit test. 我尝试了更明确和不太明确的(anyObject()),但是无论我如何尝试,此存根都不会在我的单元测试中返回fooKey。

I'm wondering if it is because it is protected or there is something else I'm missing or doing incorrectly. 我想知道是否是因为它受到保护,或者我缺少其他东西或做错了什么。 I have other when/then statements throughout the tests that are working but for some reason here, it is not. 在整个测试中,我还有其他when / then语句在起作用,但是由于某种原因,事实并非如此。

Note: The getKeyFromStream generally uses a byteArrayInputStream, but I'm trying to match it with an InputStream, I've tried both to no avail. 注意: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;
    }
}

Unit Test 单元测试

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

The problem with your unit-test is, that you are trying to mock a method of your actual class which you want to test but you can't actually invoke a mock method as this will return null unless you declare a mocked return value on that invoked method. 单元测试的问题在于,您正在尝试模拟要测试的实际类的方法,但实际上无法调用模拟方法,因为除非您在该方法上声明了模拟的返回值,否则它将返回null。调用的方法。 Usually, you only mock external dependencies. 通常,您只模拟外部依赖项。

There are actually two ways to create test-objects: mock and spy . 实际上,有两种创建测试对象的方法: mockspy The primer one will create a new object based on the class you provided which has internal state null and also return null on every invoked method. 入门者将根据您提供的类创建一个新对象,该类的内部状态为null,并且在每个调用的方法上都返回null That's why you need to define certain return values for method invocations. 这就是为什么您需要为方法调用定义某些返回值的原因。 spy on the other hand creates a real object and intercepts method invocations if "mock definitions" are defined for certain methods. 另一方面,如果为某些方法定义了“模拟定义”,则spy会创建一个真实的对象并拦截方法调用。

Mockito and PowerMock provide two ways of defining your mocked methods: 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);

or 要么

// method 2
doReturn(answer).when(mockedObject).methodToMock(param1, param2);

The difference is, that the method 1 will execute the methods implementation while the later one won't. 所不同的是, method 1将执行方法实现,而后一种则不会。 This is important if you deal with spy objects as you sometimes don't want to execute the real code inside the invoked method but instead just replace the code or return a predefined value! 如果您要处理spy对象,这很重要,因为您有时不想执行所调用方法中的真实代码,而只是替换代码或返回预定义的值!

Although Mockito and PowerMock provide a doCallRealMethod() which you can define instead of doReturn(...) or doThrow(...) , this will invoke and execute the code within your real object and ignores any mocked method return statements. 虽然和的Mockito提供PowerMock一个doCallRealMethod()您可以定义的,而不是doReturn(...)doThrow(...)这将调用和您的真实对象中执行代码和忽略任何模拟的方法return语句。 Though, this is not that useful in your case where you want to mock a method of your class under test. 但是,在您要模拟被测类的方法的情况下,这并不是很有用。

A method implementation can be "overwritten" by 可以通过以下方法“覆盖”方法实现:

doAnswer(Answer<T>() { 
    @Override 
    public T answer(InvocationOnMock invocation) throws Throwable {
        ...
    }
)

where you simply can declare what the logic of the invoked method should be. 您可以在其中简单地声明所调用方法的逻辑。 You can utilize this to return the mock result of the protected method therefore like this: 因此,您可以利用此方法返回受保护方法的模拟结果:

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

The code above works, however note that this has the same semantic as simply declaring a return value for the getKey(...) as 上面的代码有效,但是请注意,它的语义与简单地声明getKey(...)的返回值相同getKey(...)

doReturn(mockedKey).when(sut).getKey(anyString());

Trying to modify only getKeyFromStream(...) with something like this: 尝试仅使用getKeyFromStream(...)方式修改getKeyFromStream(...)

doReturn(mockedKey)
    .when(sut).getKeyFromStream(any(InputStream.class), anyString());

without modifying getKey(...) of your System-Under-Test (SUT) won't achieve anything as the real code of getKey(...) would be executed. 如果不修改测试中的系统(SUT)的getKey(...) ,将不会实现任何目标,因为将执行getKey(...)的真实代码。 If you however mock the sut-object, you could not invoke the method in your // Act section as this would return null. 但是,如果模拟sut对象,则无法在// Act部分中调用该方法,因为这将返回null。 If you try 如果你试试

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

on a mock object, the real method woulb be called and as mentiond beforehand, this would also invoke the real implementations of getKeyFromStream(...) and getKeyStream(...) regardless what you specified as mock-method. 在模拟对象上,将调用真实方法,并且如前所述,这还将调用getKeyFromStream(...)getKeyStream(...)的实际实现,而不管您指定为模拟方法是什么。

As you probably can see by yourself, mocking methods of your actual class under test is not that useful and puts more burden to you than it provides any good. 正如您可能自己看到的那样,测试中的实际类的模拟方法不是那么有用,并且给您带来的负担超过了它提供的任何好处。 Therefore, it's up to you or your enterprise' policy if you want or need to test private/protected methods at all or if you stick to testing only the public API (which I would recommend). 因此,如果您想要或需要完全测试私有/受保护的方法,或者仅坚持测试公共API(我建议这样做),则取决于您或您企业的政策。 You also have the possibility to refactor your code in order to improve testability although the primary intent of refactoring should be to improve the overall design of your code . 尽管重构的主要目的应该是改善代码的总体设计,但是您也可以重构代码以提高可测试性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM