如何使用基于 属性 的测试库为 reducer 生成一组有效操作?
How to generate a set of valid actions for a reducer using a property based testing library?
我正在尝试生成一系列操作,其中应生成的操作取决于之前的操作。
假设我的状态是一组存储为数组的数字:
[1, 2]
并且有以下动作:
{ type: "add", value: number }
{ type: "remove", value: number }
我想在检查状态属性之前生成一系列要分派的操作。如果生成删除操作,我想确保它的值处于状态。
有效示例:
initial state: [1, 2]
[{ type: "remove", value: 1 }, { type: "remove", value: 2 }]
[{ type: "add", value: 3 }, { type: "remove", value: 3 }]
无效示例:
initial state: [1, 2]
[{ type: "remove", value: 3 }]
[{ type: "remove", value: 2 }, { type: "remove", value: 2 }]
这是否可以使用基于 属性 的测试库实现?如果可以,我将如何实现?
我正在使用 https://github.com/dubzzz/fast-check,但如果使用其他库更容易,我愿意接受其他人的示例。
是的,这非常适合基于 属性 的测试。通过快速浏览 fast-check 的文档,我可以看到三种方法:
- 做一个precondition。这将忽略生成无效操作序列的测试。
在 运行 删除操作之前,您会计算该数字包含在初始状态中的频率、之前在添加操作中的频率以及之前在删除操作中的频率。到时候你就知道能不能去掉了。
- 使用model-based testing (also here)。这似乎非常适合您的用例。每个动作将由一个
Command
表示,所有命令将简单地应用它们各自的动作,并且在它们的 check
方法中,您将验证该动作是否符合条件。
这需要建立一个模型,您需要确保该模型是对实际状态的简化,并且它使用不同的实现方法(这样您就不会在这里重新实现您的错误)。在您的示例中,这可能意味着保留 Set
个出现的数字或 Map
个它们的计数,而不是有序数组。
- 首先只生成有效序列。
这比前两种方法更有效,但通常也更复杂。但是,如果要删除的生成数字过于公正并且很少与列表中的数字匹配,则可能有必要。我在这里有两个想法:
- 以递归方式生成您的操作列表,并保留与基于模型的测试中的模型相似的模型。但是,您必须自己更新它。有了这个,您可以仅为模型中当前存在的那些数字生成删除操作。
我不确定是否 letrec
or memo
help here, whether you might need to use chain
,或者要求库作者为这个用例提供额外的功能。 (甚至可能作为基于模型的测试的一部分,其中 Command
个实例可以从当前模型动态派生?)
- 生成一个删除操作,并始终与前面具有相同编号的添加操作一起生成。生成
[add(x)]
和 [add(y), remove(y)]
列表后, 合并 这些列表以任意顺序排列,但保持每个子列表元素之间的相应顺序。
这可能是执行此操作的最优雅的方法,因为它看起来与您所在州的模型完全不同。但是,我很确定您将需要 build your own Arbitrary
来实现 randomMerge
功能 - 也许向库作者寻求帮助或请求新功能。
我正在尝试生成一系列操作,其中应生成的操作取决于之前的操作。
假设我的状态是一组存储为数组的数字:
[1, 2]
并且有以下动作:
{ type: "add", value: number }
{ type: "remove", value: number }
我想在检查状态属性之前生成一系列要分派的操作。如果生成删除操作,我想确保它的值处于状态。
有效示例:
initial state: [1, 2]
[{ type: "remove", value: 1 }, { type: "remove", value: 2 }]
[{ type: "add", value: 3 }, { type: "remove", value: 3 }]
无效示例:
initial state: [1, 2]
[{ type: "remove", value: 3 }]
[{ type: "remove", value: 2 }, { type: "remove", value: 2 }]
这是否可以使用基于 属性 的测试库实现?如果可以,我将如何实现?
我正在使用 https://github.com/dubzzz/fast-check,但如果使用其他库更容易,我愿意接受其他人的示例。
是的,这非常适合基于 属性 的测试。通过快速浏览 fast-check 的文档,我可以看到三种方法:
- 做一个precondition。这将忽略生成无效操作序列的测试。
在 运行 删除操作之前,您会计算该数字包含在初始状态中的频率、之前在添加操作中的频率以及之前在删除操作中的频率。到时候你就知道能不能去掉了。 - 使用model-based testing (also here)。这似乎非常适合您的用例。每个动作将由一个
Command
表示,所有命令将简单地应用它们各自的动作,并且在它们的check
方法中,您将验证该动作是否符合条件。
这需要建立一个模型,您需要确保该模型是对实际状态的简化,并且它使用不同的实现方法(这样您就不会在这里重新实现您的错误)。在您的示例中,这可能意味着保留Set
个出现的数字或Map
个它们的计数,而不是有序数组。 - 首先只生成有效序列。
这比前两种方法更有效,但通常也更复杂。但是,如果要删除的生成数字过于公正并且很少与列表中的数字匹配,则可能有必要。我在这里有两个想法:- 以递归方式生成您的操作列表,并保留与基于模型的测试中的模型相似的模型。但是,您必须自己更新它。有了这个,您可以仅为模型中当前存在的那些数字生成删除操作。
我不确定是否letrec
ormemo
help here, whether you might need to usechain
,或者要求库作者为这个用例提供额外的功能。 (甚至可能作为基于模型的测试的一部分,其中Command
个实例可以从当前模型动态派生?) - 生成一个删除操作,并始终与前面具有相同编号的添加操作一起生成。生成
[add(x)]
和[add(y), remove(y)]
列表后, 合并 这些列表以任意顺序排列,但保持每个子列表元素之间的相应顺序。
这可能是执行此操作的最优雅的方法,因为它看起来与您所在州的模型完全不同。但是,我很确定您将需要 build your ownArbitrary
来实现randomMerge
功能 - 也许向库作者寻求帮助或请求新功能。
- 以递归方式生成您的操作列表,并保留与基于模型的测试中的模型相似的模型。但是,您必须自己更新它。有了这个,您可以仅为模型中当前存在的那些数字生成删除操作。