【问题标题】:How to unit test method which invokes another method?如何对调用另一种方法的方法进行单元测试?
【发布时间】:2014-03-10 22:17:50
【问题描述】:

如何测试调用另一个方法的方法?在我的示例中,我想知道如何测试调用create 方法的initComponentsTypeA

public MyClass{

private List<TypeA> componentsTypeA;

public void initComponents(Config c){   
    componentsTypeA = initComponentsTypeA(c);
    //...
}

private List<TypeA> initComponentsTypeA(Config c){
    //...
    List<MyObject> someList = c.getSomeList();
    List<TypeA> localList = new ArrayList<>();
    for(MyObject mo : someList){
        localList.add(create(mo));
    }
    return localList;
}

private TypeA create(MyObject myObject){
    // ...
}
}

我知道一种解决方案是以这种方式重构代码(如下所示)。但是有必要吗?它是唯一的解决方案吗?

public MyClass{

private List<TypeA> componentsTypeA;

public void initComponents(Config c){   
     List<MyObject> myObjectList = initComponentsTypeA(c);
     componentsTypeA = create(myObjectList)
    //...
}

private List<MyObject> initComponentsTypeA(Config c){
    //...
    List<MyObject> someList = c.getSomeList();
    return someList;
}

private List<TypeA> create(List<MyObject> myObjectList){
    // ...
}
}

在第二个重构示例中,方法的名称也应根据其含义进行更改。

【问题讨论】:

  • 第一种方法有什么问题,第二种方法是如何解决的?

标签: java oop unit-testing junit mocking


【解决方案1】:

您可以使用 mockito 并创建一个返回真实方法的模拟,然后覆盖您要模拟的方法的行为,或者使用 spy,它会创建一个包装对象真实实例的模拟。

这是mockito spy的教程。

编辑

由于要模拟的方法是私有的,并且正如@fge 所提到的,您需要暴露私有方法的模拟库。而powermock,就是这样做的。

【讨论】:

  • 但是,它不会监视私有方法;你需要 PowerMockito。
  • 创建一个被测类的模拟违背了单元测试的全部目的。你最终会测试一个模拟而不是你的类的逻辑。
  • 我同意。当您发现自己正在考虑模拟被测类时,这意味着可能应该重新审视设计本身。但是有时,如果您要向现有代码添加功能并且无论如何都必须测试添加的内容,那么您将无法承受。在这种情况下,我宁愿以一种草率的方式进行测试,为了纯粹性而使代码未经测试。
  • 模拟被测类通常比没有测试更糟糕。大量资源用于完全误导的结果。
  • @ylabidi 我同意有时你需要做出这样艰难的选择。人们需要真正考虑长期影响,因为我通常发现从长远来看,草率的测试会让你付出代价……而测试私有方法只会使测试变得非常脆弱。在可以更改设计之前,将方法公开可能会更好,即使这样说我很痛苦......
【解决方案2】:

鉴于initComponentsTypeA 是私有方法,您不会直接对其进行单元测试。您只想对您的公共方法进行单元测试,然后使用您的私人成员。您可以通过查看调用私有方法所产生的副作用来确认私有方法的效果。

因此,在您的情况下,您将测试 initComponents。为了查看initComponents 中发生的情况,您可能需要传入一个模拟 Config 对象,以便验证它是否被正确使用并控制它在调用getSomeList 时返回的内容。您可以通过查看您对initComponents 的调用对MyClass 的其他属性的影响来确认initComponentsTypeAcreate 的影响。

如果您无法查看对MyClass 的更改,则需要考虑重构您的类以使其成为可能。您需要将您的测试视为从另一个将使用MyClass 的类的角度来测试MyClass。如果该类无法看到或从调用initComponents 的效果中受益,那么它对任何人都没有多大用处。

希望这不是一个太模糊的答案...如果您希望我澄清我上面所说的任何内容,请告诉我。

【讨论】:

  • 这和我非常相似的答案一样模糊。 :)
  • @TonyHopkinson 是的,如果不花时间重新编写问题中的测试,很难写出一个不含糊的答案......我只是没有时间这样做片刻。 :-)
  • 我没有发现它含糊不清。看起来 OP 没有使用 TDD 来实现设计,或者走捷径。
  • @TonyHopkinson 我同意...基于 MyClass 的当前设计,我非常怀疑 TDD 是否用于创建这样的类。看起来 OP 正在尝试将测试改进到设计不佳的类上。
【解决方案3】:

你需要选择。 要么你将所有私有的东西委托给其他类,方法现在是公开的,你的问题就消失了。

或者你说所有私有的东西都是私有的,因为你不测试实现。对 initComponents 的任何测试都必须是间接测试。

如果从单元测试的角度来看它是私有的,您可以摆脱、重命名或以其他方式调整 MyClass 的实现,并希望对其行为没有影响。

这是一个黑匣子,或者不是……

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多