2

我们对订单系统进行了状态测试。有一个Arbitrary将生成一个Order具有多个LineItem' 的对象。

有以下措施:

  • 创建一个Order
  • 取消一个LineItem

创建订单的操作需要订单本身,例如:

Arbitraries.defaultFor(Order.class).map(CreateOrderAction::new)

动作的状态了解所有创建的订单。

要取消 a LineItem,我们需要了解创建了哪些订单。在内部CancelLineItemAction执行以下操作是否安全?

LineItem line = Arbitraries.<Collection<Order>>of(state.orders())
                           .flatMap(order -> Arbitraries.<Collection<LineItem>>of(order.lineItems()))
                           .sample();

基于 的 javadoc Arbitrary.sample(),它似乎是安全的,但是在有状态测试的文档中没有明确提到这个结构,我们不想仅仅为了破坏测试的可重复性而广泛使用它。

4

1 回答 1

1

TLDR

  • Arbitrary.sample()并非旨在以这种方式使用
  • 我建议使用随机取消索引,对订单项的数量取模

1. 为什么不推荐使用 Arbitrary.sample()

Arbitrary.sample()旨在用于属性之外,例如试验生成的值或在 JUnit Jupiter 等其他上下文中使用它。至少有三个原因:

  • 用于生成值的底层随机种子取决于采样前发生的情况。因此,结果并不是真正可重现的。
  • 采样不会考虑任何可能改变生成内容的添加域上下文。
  • sample()DO NOT PARTICIPATE IN SHRINKING生成的值

2. 选项1:交出一个随机对象并用于生成

在生成 CancelLineItemAction 时提交一个 Random 实例:

Arbitraries.random().map(random -> new CancelLineItemAction(random))

使用 random 调用生成器:

LineItem line = Arbitraries.of(state.orders())
           .flatMap(order -> Arbitraries.of(order.lineItems()))
           .generator(100).next(random).value();

但实际上,这与你想做的事情非常相关。这是一个简化:

3. 选项 2:交出一个 Random 对象并用它来挑选一个 line item

与上面相同,但不要绕道采样:

List<LineItem> lineItems = state.orders().stream()
                                .flatMap(order -> order.lineItems().stream())
                                .collect(Collectors.toList());

int randomIndex = random.nextInt(lineItems.size());
LineItem line = lineItems.get(randomIndex);

选项 1 和 2 将(希望)在 jqwik 的生命周期中表现合理,但它们不会尝试任何收缩。这就是为什么我推荐下一个选项。

4. 选项 3:提交取消索引并将其与行项目数取模

要生成动作:

Arbitraries.integer().between(0, MAX_LINE_ITEMS)
                     .map(cancelIndex -> new CancelLineItemAction(cancelIndex))

在行动中使用它:

List<LineItem> lineItems = state.orders().stream()
                                .flatMap(order -> order.lineItems().stream())
                                .collect(Collectors.toList());

int randomIndex = cancelIndex % lineItems.size();
LineItem line = lineItems.get(randomIndex);

此处更详细地描述了该方法:https ://blog.johanneslink.net/2020/03/11/model-based-testing/

五、未来展望

在或多或少的未来,jqwik 可能允许在生成动作时交出当前状态。这将使像你这样的东西更简单一些。但是这个功能还没有被优先考虑。

于 2020-12-03T08:05:31.610 回答