【问题标题】:How to generate a set of valid actions for a reducer using a property based testing library?如何使用基于属性的测试库为 reducer 生成一组有效操作?
【发布时间】:2020-01-08 11:57:17
【问题描述】:

我正在尝试生成一系列动作,其中应该生成的动作取决于之前的动作。

假设我的状态是一组存储为数组的数字:

[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,但如果使用另一个库更容易,我愿意接受其他人的示例。

【问题讨论】:

  • 您的意思是不想运行删除操作,除非您可以仅基于之前的操作验证该值已处于状态?
  • 这是其中的一部分。我想我可以通过基于初始状态生成动作来实现这一点(删除动作必须包含当前处于状态的值),然后在实际运行之前检查动作是否有意义(如果值不是状态,不要分派删除)。但是,这永远不会在我的问题中生成第二个有效示例,因为 3 不在初始状态。

标签: javascript property-based-testing


【解决方案1】:

是的,这非常适合基于属性的测试。通过快速浏览快速检查的文档,我可以看到三种方法:

  • 创建一个precondition。这将忽略生成无效操作序列的测试。
    在运行删除操作之前,您将计算初始状态中包含该数字的频率、现在之前添加操作的频率以及之前删除操作的频率。然后你会知道你是否可以再次删除它。
  • 使用model-based testing(也可以使用here)。这似乎非常适合您的用例。每个动作将由一个Command 表示,所有命令都将简单地应用它们各自的动作,并在它们的check 方法中验证该动作是否符合条件。
    这需要构建一个模型,您需要确保该模型是实际状态的简化并且它使用不同的实现方法(这样您就不会在这里重新实现您的错误)。在您的示例中,这可能意味着保留出现数字的 Set 或它们的计数的 Map,而不是有序数组。
  • 首先只生成有效序列。
    这比前两种方法效率更高,但通常也更复杂。但是,如果要删除的生成数字过于公正并且很少匹配列表中的数字,则可能有必要。我在这里有两个想法:
    • 以递归方式生成您的操作列表,并保留一个与基于模型的测试相似的模型。但是,您必须自己更新它。有了这个,您可以只为模型中当前存在的数字生成删除操作。
      我不确定letrec or memo 在这里是否有帮助,您是否可能需要使用chain,或者要求库作者为此用例提供额外的功能。 (甚至作为基于模型的测试的一部分,Command 实例可以从当前模型动态派生?)
    • 始终与前面具有相同编号的添加操作一起生成删除操作。在生成[add(x)][add(y), remove(y)] 列表的列表后,以任意顺序合并这些列表,但保持每个子列表的元素之间的相应顺序。
      这可能是最优雅的方法,因为它看起来不像您所在州的模型。但是,我很确定您需要 build your own Arbitrary 来获得 randomMerge 功能 - 可以向库作者寻求帮助或请求新功能。

【讨论】:

    猜你喜欢
    • 2017-04-16
    • 2020-04-27
    • 1970-01-01
    • 2020-03-06
    • 2016-06-22
    • 2022-01-16
    • 2019-01-11
    • 2016-04-11
    • 2018-06-20
    相关资源
    最近更新 更多