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 可能允许在生成动作时交出当前状态。这将使像你这样的东西更简单一些。但是这个功能还没有被优先考虑。