【问题标题】:Assertions for non-deterministic behavior非确定性行为的断言
【发布时间】:2018-02-13 19:21:34
【问题描述】:

AssertJ(或 JUnit)是否有办法在单个(流畅的)表达式中链接同一被测单元上的多个断言,其中一个断言可能会抛出异常。本质上,我试图断言:

如果被测单元 (X) 没有导致特定异常,则它 可能,然后断言被测单元上的特定属性不成立。否则断言异常属于某种类型。

例如,有没有办法表达以下错误代码可能EITHER导致异常或出现strings.size() != 10000:

@Test/*(expected=ArrayIndexOutOfBoundsException.class)*/
public void raceConditions() throws Exception {


    List<String> strings = new ArrayList<>(); //not thread-safe

    Stream.iterate("+", s -> s+"+")
    .parallel()
    .limit(10000)
    //.peek(e -> System.out.println(e+" is processed by "+ Thread.currentThread().getName()))
    .forEach(e -> strings.add(e));

    System.out.println("# of elems: "+strings.size());
} 

AssertJ 有一个soft assertions 的概念,是在这样的场景中使用的吗?如果是这样,我会很感激一些代码示例。

或者也许有更好的框架专门为这种类型的场景设计?

谢谢。

【问题讨论】:

  • 我认为这里同时发生了两件事。您正在尝试强制竞争条件,并且您正在尝试测试仅在发生异常时才发生的事情,而这可能不会发生。单元测试应该是确定性的。如果您正在编写非确定性测试,那么您不是在编写单元测试,而是在编写其他内容。检查竞争条件很重要,但通常这是压力测试或模糊测试,而不是单元测试。单元测试应该产生一致的、可预测的结果。压力测试会产生统计数据。
  • 这可能是一个选择吗? if(yourConditionIsNotMet) {Assert.fail(SomeCustomException)},这个SomeCustomException 表示比赛
  • @tadman 从某种意义上说,它们是确定性的,我期望在这两种情况下都会出现一致且可预测的失败。开放是一种失败。有什么东西可以断言存在竞争条件吗?
  • 我认为您不应该测试竞争条件。您应该测试您的操作在正常条件下是否按预期执行。如果这是一个问题,二级压力测试框架可以摆脱竞争条件。这就是你可以滥用系统到故障点的地方,这样你就可以理解你的代码可能爆炸的所有方式。其中一些故障可能是错误,其中一些错误可能需要单元测试来验证它们已得到修复。
  • 这没有任何意义。非确定性意味着非确定性。无法保证此代码表现出这两种行为中的任何一种。结果列表的size() 方法完全有可能返回10000,这并不意味着列表处于有效、一致的状态。它可能包含错误排序的元素、虚假的nulls 和重复项,甚至是比报告大小短的数组。但即使有一个完全正确的列表结果也是可能的结果之一。可能是代码永远不会完成。这是不确定的

标签: unit-testing junit java-8 race-condition assertj


【解决方案1】:

我不确定这是否是您真正想要的,但您可以尝试使用假设。 执行完被测代码后,对结果进行假设,以下代码/断言只有在假设正确的情况下才会执行。

由于 3.9.0 AssertJ 提供了开箱即用的assumptions,例如:

List<String> strings = new ArrayList<>(); // not thread-safe

int requestedSize = 10_000;

Throwable thrown = catchThrowable(() -> {
  Stream.iterate("+", s -> s + "+")
        .parallel()
        .limit(requestedSize)
        .forEach(strings::add);
});

// thrown is null if there was no exception raised
assumeThat(thrown).isNotNull();

// only executed if thrown was not null otherwise the test is skipped.
assertThat(thrown).isInstanceOf(ArrayIndexOutOfBoundsException.class);

如果您正在测试异步代码,还应该查看https://github.com/awaitility/awaitility

【讨论】:

    【解决方案2】:

    我想出了以下内容:

     @Test
     public void testRaceConditions() {
        List<String> strings = new ArrayList<>(); //not thread-safe
    
        int requestedSize = 10_000;
    
        Throwable thrown = catchThrowable(() -> { 
        Stream.iterate("+", s -> s+"+")
        .parallel()
        .limit(requestedSize)
        //.peek(e -> System.out.println(e+" is processed by "+ Thread.currentThread().getName()))
        .forEach(e -> strings.add(e));
        });
    
        SoftAssertions.assertSoftly(softly -> {
             softly.assertThat(strings.size()).isNotEqualTo(requestedSize);
             softly.assertThat(thrown).isInstanceOf(ArrayIndexOutOfBoundsException.class);
         });
    
     }
    

    如果有人拥有更多 AssertJ 或其他一些工具知道更好的方法,我很乐意接受他们的解决方案。谢谢!

    【讨论】:

      猜你喜欢
      • 2020-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多