在 Action 中使用 Arbitrary.sample 是否可重现?
Is it reproducible to use Arbitrary.sample from within an Action?
我们对订单系统进行了状态测试。有一个 Arbitrary
将生成一个 Order
对象,该对象具有多个 LineItem
。
有以下操作:
- 创建
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。为什么不推荐Arbitrary.sample()
Arbitrary.sample()
被设计为在属性之外使用,例如试验生成的值或在 JUnit Jupiter 等其他上下文中使用它。至少有三个原因:
- 用于生成值的基础随机种子取决于发生的情况
取样前。因此,结果并不是真正可重现的。
- 抽样不会考虑任何添加的可能会改变内容的域上下文
正在生成。
sample()
生成的值不参与收缩
2。方案一:递交一个Random对象,用于生成
在生成 CancelLineItemAction 时提交一个 Random 实例:
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:提交一个 Random 对象并使用它来挑选订单项
同上但采样不走弯路:
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 可能会允许在生成动作时提交当前状态。这将使像您这样的东西变得更简单。但是这个功能还没有被优先考虑。
我们对订单系统进行了状态测试。有一个 Arbitrary
将生成一个 Order
对象,该对象具有多个 LineItem
。
有以下操作:
- 创建
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。为什么不推荐Arbitrary.sample()
Arbitrary.sample()
被设计为在属性之外使用,例如试验生成的值或在 JUnit Jupiter 等其他上下文中使用它。至少有三个原因:
- 用于生成值的基础随机种子取决于发生的情况 取样前。因此,结果并不是真正可重现的。
- 抽样不会考虑任何添加的可能会改变内容的域上下文 正在生成。
sample()
生成的值不参与收缩
2。方案一:递交一个Random对象,用于生成
在生成 CancelLineItemAction 时提交一个 Random 实例:
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:提交一个 Random 对象并使用它来挑选订单项
同上但采样不走弯路:
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 可能会允许在生成动作时提交当前状态。这将使像您这样的东西变得更简单。但是这个功能还没有被优先考虑。