Java Mockito 当/然后不返回预期值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31413337/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 11:05:24  来源:igfitidea点击:

Mockito when/then not returning expected value

javaunit-testingmockitostubbing

提问by Chad

I'm trying to stub this getKeyFromStream method, using the 'any' matchers. 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.

我正在尝试使用“任何”匹配器来存根这个 getKeyFromStream 方法。我试过更加明确和不那么明确(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 workingbut 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);
}

采纳答案by Roman Vottner

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. Usually, you only mock external dependencies.

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

There are actually two ways to create test-objects: mockand spy. The primer one will create a new object based on the class you provided which has internal state null and also return nullon every invoked method. That's why you need to define certain return values for method invocations. spyon the other hand creates a real object and intercepts method invocations if "mock definitions" are defined for certain methods.

实际上有两种方法可以创建测试对象:mockspy. 初级将根据您提供的类创建一个新对象,该类具有内部状态 null 并且还会null在每个调用的方法上返回。这就是为什么您需要为方法调用定义某些返回值的原因。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 1will execute the methods implementation while the later one won't. This is important if you deal with spyobjects 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!

不同之处在于,后者method 1将执行方法实现,而后者则不会。如果您处理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. Though, this is not that useful in your case where you want to mock a method of your class under test.

尽管 Mockito 和 PowerMock 提供了doCallRealMethod()您可以定义的 a 代替doReturn(...)or doThrow(...),但这将调用和执行您的真实对象中的代码并忽略任何模拟方法返回语句。但是,在您想要模拟被测类的方法的情况下,这不是很有用。

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(...)作为

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

Trying to modify only getKeyFromStream(...)with something like this:

尝试仅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. If you however mock the sut-object, you could not invoke the method in your // Actsection as this would return null. If you try

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

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.

模拟对象上,真正的方法woulb调用和mentiond提前,这也将调用真正的实现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). You also have the possibility to refactor your codein order to improve testability although the primary intent of refactoring should be to improve the overall design of your code.

正如您自己可能看到的那样,您的实际测试类的模拟方法并没有那么有用,并且给您带来的负担比它提供的任何好处都多。因此,如果您想要或需要测试私有/受保护的方法,或者您坚持只测试公共 API(我建议这样做),这取决于您或您的企业的政策。您还可以重构代码以提高可测试性,尽管重构的主要目的应该是改进代码的整体设计