【问题标题】:How to test function with many initial checks如何通过许多初始检查来测试功能
【发布时间】:2020-10-16 16:37:37
【问题描述】:

假设我们有函数foofoo 在其实际代码运行之前检查几个初始条件,这些初始条件被布置为连续的 if 条件。如果发生错误,则会通过警报/toast 通知用户。 if 的排列方式如下:

function foo() {
  if (!condition_one) {
    alert(text_one);
    return;
  }

  if (!condition_two) {
    alert(text_two);
    return;
  }

  if (!condition_three) {
    alert(text_three);
    return;
  }

  if (!condition_four) {
    alert(text_four);
    return;
  }

  // ...
}

在编写这个函数之前,我们按照 TDD 原则编写了我们的第一个单元测试。第一个测试检查condition_one 失败的情况。第二个测试检查condition_one 是否成功,即text_one 没有收到警报。

我们现在复制第一个测试用例并对其进行转换,使其成为检查condition_two 失败的测试。我们必须为此扩展第一个测试用例,因为我们需要让第一个条件成功才能达到第二个条件。我们现在为condition_two 编写后续测试并重复该过程,直到我们测试完所有初始条件。

问题是,每次我们进入下一个条件时,所有先前条件的所有设置代码都会累积,并且该条件的实际设置代码会在混乱中丢失,甚至很难知道我们正在测试什么.

这个问题有哪些可能的解决方案?我知道这正是面向方面编程试图解决的问题,但这不是我的选择。

PS:这个问题也出现在其他大型 if-else 结构中,因此比这个特定场景更适用。

【问题讨论】:

  • 请添加测试代码以及​​您编写前 4 或 5 个测试和代码的顺序的分步示例。
  • 我发现在函数中包装条件并将其存根应该是正确的答案

标签: unit-testing tdd organization


【解决方案1】:

设计是我们所做的,是为了得到我们想要的东西,而不是仅仅这样做。

答案是我们需要为我们的测试考虑“我们想要什么”,然后实现它。

在这种情况下,“您想要什么”之一就是能够将测试的重要细节与背景噪音区分开来。这通常是通过将背景噪音移出测试主体来实现的。

概括地说,你的第三个测试大致是这个形状

assume condition_one
assume not condition_two
foo()
assert alert(text_two)

在这个描述中隐含的事实是,我们不关心除了 condition_one 和 condition_two 之外的任何东西。

所以在代码中,可能看起来像

InitialConditions.any()
InitialConditions.condition_one(true)
InitialConditions.condition_two(false)

foo()

assert alert(text_two)

随着您的进展,Given(无论是什么)变得越来越复杂,以便您更准确地表达每个测试的假设 - 但是每个测试的散文仍然基本上与细节的复杂性成线性关系,而不是整体的复杂性。

Nat Pryce on Test Data Builders (2007) 是一本不错的入门读物。

【讨论】:

    【解决方案2】:

    就测试而言,如果不遵循Single Responsibility Principle (SRP),则无法正确测试每个功能。从foo 函数中可以看出,条件太多,因此,有一些原因要更改此方法。在这种方法中,编写测试太难了,有时甚至是不可能的。

    我敦促重构这种方法以坚持 SRP,然后编写测试将是小菜一碟。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-01
      • 2021-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-24
      • 1970-01-01
      相关资源
      最近更新 更多