【问题标题】:Assert that Optional has certain value断言 Optional 具有一定的价值
【发布时间】:2016-08-15 12:04:05
【问题描述】:

我有一个返回Optional 的Java 方法。 我想为它写一个易于阅读的单元测试来断言

  1. 返回的 Optional 有一个值(即 Optional 不为空)并且

  2. 返回值等于预期值。

假设我测试的方法是

Optional<String> testedMethod(){
  return Optional.of("actual value");
}

【问题讨论】:

  • 我会简单地使用assertEquals("actual value", testedMethod().get()); 如果可选为空,它将失败并抛出异常,这足以让您的单元测试失败,所以您真的不需要更多。

标签: java unit-testing optional


【解决方案1】:

您也可以使用AssertJ 进行流畅的断言

@Test
public void testThatOptionalIsNotEmpty() {
    assertThat(testedMethod()).isNotEmpty();
}

@Test
public void testThatOptionalHasValue() {
    assertThat(testedMethod()).hasValue("hello");
}

【讨论】:

    【解决方案2】:

    TL;DR 最佳整体方法是由 Ole V. V. 提出的:

    assertEquals(Optional.of("expected"), opt);
    

    下面讨论其他替代方案。

    有几种不同的方法可以做到这一点,具体取决于您对测试结果的清晰性和测试写作的简洁性的喜好。对于这个答案,我将坚持使用“库存”Java 8 和 JUnit 4,没有其他依赖项。

    正如comment by Ole V.V. 中所建议的那样,一种方法就是简单地写

    assertEquals("expected", opt.get());
    

    这通常有效,但如果 Optional 为空,则 get() 将抛出 NoSuchElementException。这反过来会导致 JUnit 发出错误而不是失败的信号,这可能不是您想要的。除非您已经知道 get() 在这种情况下抛出 NSEE,否则也不太清楚发生了什么。

    另一种选择是

    assertTrue(opt.isPresent() && "expected".equals(opt.get()));
    

    这也很有效,但如果存在不匹配,它不会报告实际值,这可能会给调试带来不便。

    另一种选择是

    assertEquals("expected", opt.orElseThrow(AssertionFailedError::new));
    

    这会给出正确的失败并在不匹配时报告实际值,但对于抛出AssertionFailedError 的原因并不是很明确。您可能不得不盯着它看一会儿,直到您意识到当 Optional 为空时会抛出 AFE。

    还有一个选择是

    assertEquals("expected", opt.orElseThrow(() -> new AssertionFailedError("empty")));
    

    但这开始变得冗长了。

    你可以把它分成两个断言,

    assertTrue(opt.isPresent());
    assertEquals("expected", opt.get());
    

    但您之前曾因为冗长而反对this suggestion。在我看来,这并不是非常冗长,但它确实有一些认知开销,因为有两个单独的断言,并且它依赖于仅在第一个成功时检查第二个。这并没有错,但有点微妙。

    最后,如果您愿意创建一些您自己的基础架构,您可以创建一个适当命名的AssertionFailedError 子类并像这样使用它:

    assertEquals("expected", opt.orElseThrow(UnexpectedEmptyOptional::new));
    

    最后,在另一条评论中,Ole V. V. 建议

    assertEquals(Optional.of("correct"), opt);
    

    这很好用,事实上这可能是最好的。

    【讨论】:

    • 如果您接受null 作为不存在值的另一种常规表示,那么还有assertEquals("correct", opt.orElse(null));。这不是我个人的喜好,但如果是你的,请继续。
    • 再想想,还有assertEquals(Option‌​al.of("corre‌​ct"), opt);。它很简洁,虽然我花了一段时间才想到写它,但读起来没问题,不是吗?我还希望它能够在失败时提供清晰、清晰的消息,无论是在 opt 持有错误值还是为空的情况下。
    • 我认为最后一种方式绝对是最清晰的方式,也是最“正确”的方式。考虑如何测试列表或地图的内容。没有理由这应该有什么不同。
    • 我认为如果将 Ole V. V. 的简短最新更新移到答案的顶部会更好。这是最重要的部分,不应该在这么长的答案中持续下去
    • 如果您尝试在没有“isPresent()”检查的情况下使用“get()”,IDE 将抛出一个对我不利的警告
    【解决方案3】:

    我为此使用Hamcrest Optional

    import static com.github.npathai.hamcrestopt.OptionalMatchers.hasValue;
    import org.junit.Test;
    
    public class MyUnitTests {
    
      @Test
      public void testThatOptionalHasValue(){
        String expectedValue = "actual value";
        assertThat(testedMethod(), hasValue(expectedValue));
      }
    }
    

    您可以将 Hamcrest Optional 添加到您的依赖项中,方法是将其包含在您的 build.gradle 中:

    dependencies {
      testCompile 'junit:junit:4.12'
      testCompile 'com.github.npathai:hamcrest-optional:1.0'
    }
    

    【讨论】:

      【解决方案4】:

      为什么不使用isPresent()get()

      【讨论】:

      • 这是两个断言,会导致非常冗长的单元测试。我正在寻找更简洁的东西。
      • @MatthiasBraun 如果他们测试一个概念,那么拥有两个断言并没有什么不好。我同意它不是很简洁
      【解决方案5】:

      以下方法使用了您可以为可选选项指定默认返回的事实。所以你的测试方法可能是这样的:

      @test
      public void testThatOptionalHasValue() {
          String expectedValue = "actual value";
          String actualValue = Optional.ofNullable(testedMethod()).orElse("not " + expectedValue);
          assertEquals("The values are not the same", expectedValue, actualValue);
      }
      

      这保证了如果你的方法返回null,那么结果不能和期望值一样。

      【讨论】:

        【解决方案6】:

        我更喜欢在断言之前将 .orElse(null) 应用于返回的 Optional。

        方法 assertEquals 可以与包含预期值的 Optional 一起使用(或使用 FEST 断言时的 assertThat(result).isEqualTo(expected))。

        但使用 .orElse 的好处是结果具有正确的类型,并且如果该方法不是可选的,则允许执行您可能想要执行的所有检查:

        • == 运算符(或使用 FEST 时的 isSame 方法)
        • compareTo 和使用 compareTo 的方法
        • startsWith、endsWith、包含、长度(如果是字符串)
        • ...

        使用期望值的 Optional 不允许所有这些事情。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-04-20
          • 1970-01-01
          • 1970-01-01
          • 2018-10-26
          • 1970-01-01
          • 1970-01-01
          • 2014-10-21
          • 2019-09-07
          相关资源
          最近更新 更多