在诉讼中使用仲裁样本是否可重复



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

有以下操作:

  • 创建Order
  • 取消LineItem

创建订单的操作本身就是订单,例如:

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

操作的状态具有关于所有创建的订单的知识。

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

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

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

TLDR

  • Arbitrary.sample()并非设计为以这种方式使用
  • 我建议使用对行项目数取模的随机取消索引

1.为什么不建议使用任意.sample((

Arbitrary.sample()被设计为在属性之外使用,例如,对生成的值进行实验或在JUnit Jupiter等其他环境中使用。至少有三个原因:

  • 用于生成值的底层随机种子取决于发生的情况采样前。因此,结果并不是真正可重复的
  • 采样不会考虑任何可能改变正在生成
  • sample()生成的值不参与收缩

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

生成CancelLineItemAction:时交一个随机实例

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

使用随机调用生成器:

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

但实际上,这与你想做的事情有关

3.选项2:交一个随机对象并使用它来拾取行项目

与上述相同,但采样时不要绕路:

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/

5.未来展望

在或多或少遥远的将来,jqwik可能允许在生成动作时交出当前状态。这会让像你这样的东西变得简单一点。但这一功能尚未得到优先考虑。

相关内容

最新更新