【问题标题】:How to test(mock) object that uses external API (Jama Software)如何测试(模拟)使用外部 API 的对象(Jama Software)
【发布时间】:2016-06-21 14:23:16
【问题描述】:

我在尝试使用外部 API 测试 Class 时遇到问题 -

Jama 软件

。 假设有一个ClassA。它有 2 种方法,我想使用 JUnit 进行测试。 这两种方法都有ClassB 作为参数。

ClassB 属于另一个 API。我可以看到它的方法,但没有实现细节。(compiled code)。问题是:如何模拟这些类以使 TestClass 隔离?我知道在Mockito 中有hardcode 响应的选项。是否有任何更好/更清洁 的方法可以做到这一点,所以我不需要手动配置模拟对象?如果您提供有关该主题的任何建议,我将不胜感激。

【问题讨论】:

  • 最干净的解决方案是实例化/获取第三方类的有效对象,并在测试中使用它们。不要嘲笑他们,除非你遇到了一些技术难题,如果没有嘲笑就无法令人满意地解决。相信我(10 年的经验),嘲笑最好留给特殊情况。
  • 所以基本上我们不能在这种情况下隔离我们的测试?像我们通常在项目中那样使用 3 方软件?
  • 除非您进行集成测试,否则如果 B 类对象,我不会使用真实对象 - 特别是如果您没有源代码。你不想测试classB - 你想测试你的classA。硬编码响应是您想要定义的测试配置来测试您的课程。如果它很难配置模拟 - 如果它做得太多或违反 demeter 法则,我会看看该方法。
  • 所以我有 3 个 pardy 类及其接口,我不知道它的实现。为了隔离测试,我可以模拟这个类,并且需要手动配置它来设置每个方法应该返回的东西。如果我想实现隔离,我无法避免这项工作。我很困惑,因为有一些方法可以模拟数据库而不是在真实数据库上操作,这就是我试图确定是否有任何简单的方法来配置它。
  • 您能否提供一些信息,违规类是做什么的?选择何时模拟、何时使用真实对象或何时编写假对象在很大程度上取决于手头的任务。特别是数据存储可能是伪造而不是模拟的条件,而模拟在模拟异常行为方面具有优势。

标签: java unit-testing junit mocking mockito


【解决方案1】:

A good read about mocking 来自另一个答案。

使用 mock 及其方法的目的是将其与其依赖项隔离开来。模拟参数可能看起来很脏或不花哨,但它旨在让您测试模拟类的期望。在这种情况下,您假设无论您的服务 ClassB 是什么,都按预期工作(或者,如果您也期望异常)。在这种情况下,我们正在测试一个单个单元 ClassA 并假设 ClassB 是正确的。

我会继续做一个更具体的例子,类似于上面的服务员/厨师的例子。

假设您的 ClassA 是 CookingPot,而 ClassB 是 Pantry。每当您与 Pantry 互动以获取面食时,它都会尝试向您发送面食。如果它没有 Pasta,则返回 null。

Pantry 是一个花哨的 Pantry。它有一个存储其内容的表,因此您不知道它是否有 Pasta。这在现实世界中很“复杂”,但这不是我们的储藏室。我们不知道它是如何工作的,只是它要么返回 Pasta,要么不返回。我们不知道逻辑,也我们必须。我们知道它的作用,所以如果我们想确保可以在锅中添加意大利面,我们可以使用具有相同作用的模拟食品储藏室,而无需真正的花哨。

Pantry 还有很多其他方法,例如允许您将剩余的 Pasta 添加回货架。您不知道智能 Pantry 是如何将意大利面放回原处的,但您不需要对其建模,因为 Mockito 无论如何都可以处理 void 调用。

如果 ClassB 发生变化,这些测试可能仍会通过,但您会在集成测试中发现问题并说“嗯,我的课程仍然有效,但新的 Pantry 出了点问题,所以我需要重做课程,并重新连接测试。”

CookingPot {
    void addPasta(Pantry pantry) {
        Pasta p = pantry.getPasta();
        System.out.println("Added " + p.toString() + " to the pot!"); 
        //fancy actions that use up some of the Pasta
        pantry.add(p); //return the pasta
    }
}

Pantry {
    Pasta getPasta(); //we have no idea what this does in actuality
}


@Test(expected = NullPointerException.class)
public void testAddPastaButNoPastaLeft() {
    Pantry mockPantry = mock(Pantry.class);

    CookingPot cookingPot = new CookingPot();
    cookingPot.addPasta(mockPantry);
    //we should probably have checked if there was pasta before 
    //attempting to add imaginary pasta
}

@Test
public void testAddPasta() {
    Pantry mockPantry = mock(Pantry.class);
    when(mockPantry.getBaz()).thenReturn(new Pasta());

    CookingPot cookingPot = new CookingPot();
    cookingPot.addPasta(mockPantry);
}

【讨论】:

  • 感谢您的回复。不幸的是我不明白两件事。你给了 ClassA 和 ClassB 新的名字,但是在 testAddPasta() 我看到 mocmClassB.getBaz() 我不知道这是什么情况:)
  • @Dago 错字。我原本打算使用基本的类名,但读起来很乱。基本上,您可以模拟 API 调用。但是,通常,您希望将自己的 Pantry 对象作为使用 JamaPantry 的服务。这样,如果 JamaPantry 发生变化,您只需将 Pantry 更改为处理 JamaPantry,而不是更改其他所有内容来处理 JamaPantry。
  • 好的,我试着说清楚:) 我想知道是否有可能将我的测试类与其他依赖项隔离开来,模仿 API 的类调用而不自己设置期望,但模拟 API 行为并连接对他们来说,实际上,,?如果我会使用使用 ClassB 的服务并使用它来测试,那意味着我不会隔离测试而是通过该特定服务注入依赖项?
猜你喜欢
  • 2015-03-20
  • 2016-03-06
  • 1970-01-01
  • 2019-02-17
  • 1970-01-01
  • 2020-08-24
  • 2020-07-04
  • 2011-08-11
  • 2021-04-12
相关资源
最近更新 更多