【问题标题】:How can I unit test void functions?如何对 void 函数进行单元测试?
【发布时间】:2014-02-08 19:01:52
【问题描述】:
class Elephant extends Animal {   
    public Elephant(String name) {
        super(name);
    }

    void makeNoise() {
        logger.info(" Elephant  make Sound");
    }

    void perform(String day) {
        if (day.equals("thursday") || day.equals("friday")) {
            makeNoise();
        }
    }
}

现在我想测试perform 方法。如何使用 JUnit 对该方法进行单元测试?

【问题讨论】:

  • logger 来自哪里?
  • 可能来自Animal ;)
  • 你需要一个模拟框架。去谷歌上查询。我会推荐 Mockito,但我可能有点偏颇,因为我与该产品有一些联系。
  • 我在 Animal 类中使用的记录器,这很好。我想使用 junit 测试所以我在哪里使用

标签: java unit-testing junit


【解决方案1】:

Mockito Spy 解决方案

import org.junit.Test;

import static org.mockito.Mockito.*;

public class ElephantTest {

    @Test
    public void shouldMakeNoise() throws Exception {

        //given
        Elephant elephant = spy(new Elephant("foo"));

        //when
        elephant.perform("friday");

        //then
        verify(elephant).makeNoise();

    }
}

负面测试:

@Test
public void elephantShouldDontMakeNoisesOnMonday() {

    //given
    Elephant elephant = spy(new Elephant("foo"));

    //when
    elephant.perform("monday");

    //then
    verify(elephant, never()).makeNoise();

}

@Test
public void shouldDoNotMakeNoisesOnMonday() {

    //given
    Elephant elephant = spy(new Elephant("foo"));

    //when
    elephant.perform("monday");

    then(elephant).should(never()).makeNoise();

}

依赖

org.mockito:mockito-core:2.21.0

了解

【讨论】:

  • 简洁美观。如果您添加了大象在星期一不发出声音的负面测试用例,我会投赞成票 - 并立即开始使用 Mockito :)
  • @ckonig 我希望你已经在使用 mockito,附加了负面测试用例 ;)
【解决方案2】:

您有多种选择,具体取决于您愿意使用的工具以及您的测试应该具有的深度。

使用纯 Java 进行部分模拟

创建一个从大象扩展的类(MockElephant),覆盖makeNoise,以便计算调用次数。在您的测试中使用该类来检查 makeNoise 的调用次数是否正确

使用框架进行部分模拟 您基本上执行与上述相同的操作,但不是手动编码 MockElephant,而是使用一些模拟框架创建它。使测试更简单,因为您需要更少的代码。而且更容易阅读。但是,如果发生奇怪的事情,就很难理解发生了什么。对于 Mocking 框架,我认为它们是值得的。

这被称为部分模拟的原因是你只模拟了一个类的一部分(在这种情况下是一个方法)。

替代方法是使用 Normal Mocks,在您的情况下这似乎是可行的(但在遗留代码中可能会变得困难)。

在这里,您可以将 Logger 作为依赖项注入。例如,您可以创建一个额外的构造函数,允许您提供 Logger。然后,在您的测试中,您将使用一个模拟的 Logger,它再次计算它的调用,可能连同它收到的参数一起,并检查它是否具有预期值。

同样,您可以使用 Mocking 框架或普通的旧 Java 来做到这一点。

【讨论】:

    【解决方案3】:

    要测试任何方法,要测试的职责必须通过更改任何变量的状态从方法外部可见。

    通常是通过从方法返回值来完成的。但如果没有这个,可以通过从方法范围之外修改某些内容以多种方式完成,以防您有任何“问题”从方法返回某些内容!

    在您的情况下,您只记录一些消息。从某种意义上说,您的代码并不是真正可测试的,因为它不会做与更改任何变量的状态直接相关的事情(因为您更改了变量以外的其他资源的状态,您的代码无法直接访问该状态。您必须编写一些代码来读取来自该外部资源的更改,因此使您的测试代码也依赖于读取。如果您在阅读时遇到问题,您的测试用例将无法通过,这与单元的精神不符测试。主要思想是尽可能减少对外部代码或库的依赖)。但是您的代码可以通过进行轻微的重构/转移责任来测试,如下所示:

    String makeNoise() {
        return "Elephant  make Sound";
    }
    
    String perform(String day) {
        if (day.equals("thursday") || day.equals("friday")) {
          return makeNoise();
        }
    }
    

    然后您将记录从perform 方法返回的值的责任转移到使用它的方法上,如下所示:

     logger.info(perform(day));
    

    【讨论】:

      【解决方案4】:

      void() 函数改变程序的状态。这可以通过修改变量、文件、数据库等来完成。

      在你的情况下,你正在写一个记录器。如果这导致将“大象发出声音”写入文件,那么您可以读取该文件并查看文件中的数据是否包含您的嘈杂大象。

      但是,如果它不涉及您可以检查的任何内容(即:它只是在控制台上显示输出),那么您可能需要查看某种形式的依赖注入 (DI),您可以在其中将输出设置为文件或其他您可以轻松阅读的内容。

      应该注意的是,您可以通过模拟对象并检查是否调用了适当的方法来绕过 DI。

      【讨论】:

      • 就像我想检查执行方法,它会在星期四或星期五的输入中正确运行,那么我的 junit 测试用例中的代码是什么
      • 您似乎在寻找我无法给您的确切代码答案。不仅因为我不想;也因为我不能:我不知道logger.info(string) 做了什么。按照我在帖子中所说的做:如果它写入文件,请读取您的文件。如果它写入控制台,则重定向输出。一旦有了访问生成的状态数据的具体方法,您就可以检查结果。
      猜你喜欢
      • 2021-08-02
      • 1970-01-01
      • 2021-07-16
      • 1970-01-01
      • 2018-10-03
      • 2019-04-21
      • 2019-11-25
      • 2014-05-10
      • 1970-01-01
      相关资源
      最近更新 更多