当前位置: 首页 > 面试题库 >

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

公冶桐
2023-03-14
问题内容

我正在尝试使用“
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,除非您在该方法上声明了模拟的返回值调用的方法。通常,您只模拟外部依赖关系。

实际上,有两种创建测试对象的方法: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(...)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(我建议这样做),则取决于您或您企业的政策。尽管重构的主要目的应该是改善代码的整体设计,但是您也可以重构代码以提高可测试性。



 类似资料:
  • 我目前正在学习Spring boot,我一直在测试一个项目——非常感谢任何帮助,因为我是这里的初学者。 我有一个rest控制器测试,使用Mockito,当使用Mockito.when()调用方法时,该测试似乎忽略了Then返回。 这是整个班级: 名为“testCreateUser”的测试没有问题地通过了。给我一个问题的是名为“testFindUserById”的测试。 以下是我尝试测试的控制器方法

  • 在编写测试用例时,我使用Mockito模拟某个类。 有没有办法在返回值之前打印一些语句?例如: 上述声明有效,但我无法做到以下几点:

  • 这是我的实际服务方法。在测试时,它给我的变量为空。用户和响应 我试图写一个测试用例和模拟方法findByEmail()和保存(),但同时嘲笑我没有收到用户对象在返回中提到的thenback()和doback()。 有人能帮忙吗。

  • 我正在使用JUnit和Mockito库来测试我的应用程序。问题是,当我在代码下面执行时,值在运行时没有返回空列表,并且测试失败。理想情况下,当get执行时,它应该返回空列表 我热切期待着支持。有没有人能帮我一下,如何通过这个测试用例???。如何通过Mockito使第8行的控件通过测试用例??? 请假设,下面两个类没有真实的代码,我们只有二进制文件作为JAR文件,我们不能修改下面的代码....我附上

  • 原文是这样的: 这确实令人惊讶,但最终明白了(嗯,至少我想我明白了)为什么会这样发生。 现在,我将第二个更改为: 如果我遗漏了一些明显的东西,或者我的问题是愚蠢的,让我提前说声对不起。还有谢谢你!

  • 我是Scala新手,正在为Scala中的Play应用程序编写一些测试。Play应用程序已经用Java编写。 我有一个有一个公共方法进程的刷新服务 其中动作在另一个包中定义为POJO 我已经根据Scala指南编写了测试 当试图模拟此服务时,我使用了以下代码 我从Scala得到以下编译器错误,无法弄清楚这些类型如何是矛盾的 [错误]/home/joey/Projects/sntdb/test/cont