使用Mockito调用多个其他方法的方法的单元测试

时间:2012-09-27 15:34:52

标签: java unit-testing mockito

也许我在搜索中完全不知所措,但我无法找到与如何为Java类/方法编写单元测试相关的任何文档或讨论,而Java类/方法又调用其他非私有方法。看起来,Mockito认为,如果必须使用间谍来测试一种需要模拟内部方法调用的方法,那么设计可能存在问题(不是真正的OO)。我不确定这是永远的。但是使用间谍似乎是实现这一目标的唯一方法。例如,为什么你不能有一个“包装”样式方法,而这种方法依赖于原始功能的其他方法,但另外提供功能,错误处理,日志记录或依赖于其他方法的结果的不同分支等等。 p>

所以我的问题是双重的:

  1. 设计不良并实现代码的方法是内部调用其他方法吗?
  2. 如果选择Mockito作为他们的模拟框架,那么为这种方法编写单元测试的最佳实践和/或方法是什么(假设它本身是一个好主意)?
  3. 这可能是一个困难的要求,但我更愿意那些决定回答不仅重新发表Mockito措辞和/或对间谍的立场的人,因为我已经意识到这种方法和意识形态。另外,我也使用过Powermockito。对我来说,问题在于Mockito开发了这个框架,必须创建额外的解决方案来支持这种需求。所以我想我想要回答的问题是,如果间谍是“坏”,并且Powermockito不可用,那么应该如何对一个调用其他非私有方法的方法进行单元测试呢?

4 个答案:

答案 0 :(得分:7)

  

设计不良并且实现的代码是否有内部调用其他方法的方法?

不是真的。但是我要说的是,在这种情况下,应该测试调用其他方法的方法,就像其他未经过单独测试的方法一样。 也就是说,它可以保护您免受公共方法在没有注意到的情况下停止调用其他方法的情况。

是的,它(有时)会产生很多测试代码。我相信这就是重点:编写测试的痛苦是一个很好的线索,你可能想要考虑将这些子方法提取到一个单独的类中。

如果我可以接受这些测试,那么我认为子方法还没有被提取出来。

  

如果选择Mockito作为他们的模拟框架,那么为这种方法编写单元测试的最佳实践和/或方法(假设它本身是一个好主意)是什么?

我会做那样的事情:

public class Blah {
    public int publicMethod() {
        return innerMethod();
    }

    int innerMethod() {
        return 0;
    }
}


public class BlahTest {
    @Test
    public void blah() throws Exception {
        Blah spy = spy(new Blah());
        doReturn(1).when(spy).innerMethod();

        assertThat(spy.publicMethod()).isEqualTo(1);
    }
}

答案 1 :(得分:2)

如果你真的需要(或想要)避免再次调用低级方法,你可以将它们存根而不是模仿它们。例如,如果方法A调用B和C,则可以执行以下操作:

MyClass classUnderTest = new MyClass() {
    @Override
    public boolean B() {return true;}

    @Override
    public int C() {return 0;}
};
doOtherCommonSetUp(classUnderTest);
String result = classUnderTest.A("whatever");
assertEquals("whatIWant", result);

我在遗留代码中使用了相当多的内容,其中广泛的重构很容易导致软件版本的船舶疾病:将难以测试的东西隔离成一个小方法,然后将其存在。

但是如果被调用的方法相当无害并且不需要嘲笑,我只是让它们再次被调用而不用担心我覆盖了它们中的每一条路径。

答案 2 :(得分:2)

对我而言,这个问题与凝聚力的概念密切相关。

我的回答是:

在类中调用其他方法(私有)的方法(公共)是可以的,实际上这通常是我认为的好代码。然而,有一点需要注意的是,你的班级应该仍然具有很强的凝聚力。对我来说,这意味着你的班级的“状态”应该被很好地定义,你的班级的方法(思考行为)应该以可预测的方式改变你的班级状态。

您尝试测试的是这种情况吗?如果没有,当你应该看两个(或更多)时,你可能正在看一个班级。

您要测试的课程的状态变量是什么?

您可能会发现,在考虑了这些类型问题的答案之后,您的代码将以您认为的方式更容易进行测试。

答案 3 :(得分:2)

真正的问题应该是:

  

我真的想测试什么?

实际上答案应该是:

  

我的对象响应外部变化的行为

也就是说,根据人们与对象交互的方式,您希望在单个测试中测试每个可能的单个场景。这样,您可以确保您的班级根据您的期望做出反应,具体取决于您提供考试的方案。

  

设计不良并且实现的代码是否有内部调用其他方法的方法?

不是真的,真的不是!从公共成员调用的这些所谓的私有方法就是辅助方法。有辅助方法是完全正确的!

Helper方法有助于将一些更复杂的行为从类本身内部分解为更小的可重用代码。只有它知道它应该如何表现并通过你班级的公共成员相应地返回状态。

看到一个带有辅助方法的类是很奇怪的,通常它们必须采用一个内部行为,而该类不应该对外界做出反应。

  

如果选择Mockito作为他们的模拟框架,那么为这种方法编写单元测试的最佳实践和/或方法(假设它本身是一个好主意)是什么?

以我的拙见,你不测试那些方法。当公共成员通过公共成员呼叫期望从您的对象中进行测试时,他们会接受测试。例如,使用MVP模式,如果要测试用户身份验证,则不应测试每个私有方法,因为私有方法也可能从依赖于测试对象的对象调用其他公共方法等等。相反,测试你的观点:

@TestFixture
public class TestView {
    @Test
    public void test() {
        // arrange
        string expected = "Invalid login or password";
        string login = "SomeLogin";
        string password = "SomePassword";

        // act
        viewUnderTest.Connect(login, password);
        string actual = viewUnderTest.getErrorMessage;

        // assert
        assertEqual(expected, actual);
    }
}

此测试方法描述了视图的预期行为,比如说,单击connectButton。如果ErrorMessage属性不包含预期值,则表示您的视图或演示者的行为不符合预期。您可以检查演示者是否订阅了视图的Connect事件,或者演示者是否设置了正确的错误消息等。

事实是你永远不需要测试私有方法中发生的任何事情,因为你应该调整并对调试进行修正,这反过来会导致你同时测试内部方法的行为,但没有特别的测试方法应该明确写出那些辅助方法。