【问题标题】:What to test when writing Unit Tests?编写单元测试时要测试什么?
【发布时间】:2010-03-10 15:48:25
【问题描述】:

我想开始对我们的应用程序进行单元测试,因为我相信这是与测试建立良好关系的第一步,并将允许我进入其他形式的测试,最有趣的 BDD 与 Cucumber。

我们目前使用 Codesmith 生成所有基类,这些基类完全基于数据库中的表。我很好奇使用这些基类生成测试用例的好处?这是糟糕的测试实践吗?

这让我想到了我的帖子的终极问题。使用单元测试时我们要测试什么?

我们是否测试了我们知道我们想要的示例?还是我们测试我们不想要的示例?

它们可以是具有多种失败方式和多种成功方式的方法,我们如何知道何时停止?

以求和函数为例。在唯一的单元测试中给它 1,2 并期望 3.. 我们怎么知道 5,6 不会返回 35?

问题回顾

  • 生成单元测试(好/坏)
  • 我们测试什么/多少?

【问题讨论】:

  • 一个有趣的发现:codeplex.com/classtester 允许您对 getter/setter 进行单元测试,而无需为每个生成器生成代码行。

标签: unit-testing testing code-generation


【解决方案1】:

从您的需求开始,编写测试预期行为的测试。从那时起,您测试的其他多少场景可能取决于您的日程安排,或者可能取决于您识别出的风险特别高的不成功场景。

您可能会考虑仅针对您(或您的用户)发现的缺陷编写不成功的测试(这个想法是您在实际修复缺陷之前编写一个测试缺陷修复的测试,这样您的测试就会失败如果在将来的开发中将该缺陷重新引入您的代码中)。

【讨论】:

  • Requirements 很可能在这里被误解。这是单元测试测试,您可以在其中测试单个代码单元(单个过程等)并且[应用程序级别/客户驱动的]要求非常遥远。当然,作为程序员,我们可以为基本函数的每个 I/O/“行为”制定“要求”,但这并不是“要求”这个词通常的理解方式。
【解决方案2】:

单元测试的目的是让您确信(但只有在特殊情况下才能确定)您的公共方法的实际行为与预期行为相匹配。因此,如果你有一个班级Adder

class Adder { public int Add(int x, int y) { return x + y; } }

以及相应的单元测试

[Test]
public void Add_returns_that_one_plus_two_is_three() {
    Adder a = new Adder();
    int result = a.Add(1, 2);
    Assert.AreEqual(3, result);
}

那么这会给您一些(但不是 100%)的信心,即被测方法的行为正确。它还为您提供了一些防御,以防止在重构时破坏代码。

使用单元测试时我们要测试什么?

您的公共方法的实际行为与预期(或指定)行为的对比。

我们是否测试了我们知道我们想要的示例?

是的,获得方法正确性的一种方法是获取一些具有已知预期输出的输入,对输入执行公共方法并将实际输出与预期输出进行比较。

【讨论】:

    【解决方案3】:

    要测试的内容:曾经出错的所有内容。

    当您发现错误时,在修复代码之前为错误行为编写测试。然后,当代码正常运行时,测试将通过,您的武器库中将进行另一个测试。

    【讨论】:

      【解决方案4】:

      1) 首先,我建议您测试应用的核心逻辑。

      2)然后,使用vs中的代码覆盖工具,看看你的所有代码是否都用在了测试中(if-else的所有分支,case条件都被调用了)。 这是对您关于测试 1+2 = 3、5 + 6 = 35 的问题的某种回答:当代码被覆盖时,您可以通过进一步的实验感到安全。

      3) 覆盖 80-90% 的代码是一种很好的做法:其余工作通常效率不高:getter-setter、1 行异常处理等。

      4) 了解关注点分离。

      5) 生成单元测试 - 尝试一下,您会发现,您可以节省大量手动编写的代码。我更喜欢用vs生成文件,剩下的TestMethods自己写。

      【讨论】:

      • 我想或多或少我的生成单元测试将使用已经存在的断言生成。他们将测试 Getter/Setter。我读过测试 Getters/Setters 通常被认为是一件好事。通常在“如果一个人坏了,我肯定想知道”的情况下。想法?
      • 假设你有一个有 10 个参数的函数,你需要测试它是否有 9 个不同的异常,通过更改 1 个参数抛出。如果您将使用生成的测试,您将获得 9*(10+..) ~= 150 行代码,其中大部分是相同的。创建一个方法助手(带有 1 个参数),调用你的方法助手(带有 10 个参数),然后只调用一个 9 次,提高 yoyr 测试的可读性,这不是一个好主意吗?另一方面,要测试私有方法,编写你自己的访问器就够棘手了。当然,这应该是自动完成的。我希望,我的想法会对你有所帮助。
      • 我发现,如果我的 getter 和 setter 有足够的副作用,需要进行重要的独立测试,我已经准备好迎接痛苦了。
      • 当然,我说的是没有副作用的简单 getter-setter。
      【解决方案5】:

      你在哪里对东西进行单元测试

      • 想要确保您的算法有效
      • 希望防止将来发生意外更改

      因此,在您的示例中,测试生成的类没有多大意义。改为测试生成器。

      最好先测试主要用例(测试功能的设计目的)。然后你测试主要的错误案例。然后你为极端情况(即下限和上限)编写测试。不寻常的错误案例通常很难产生,因此对它们进行单元测试是没有意义的。

      如果您需要验证大量参数集,请使用数据驱动测试。

      你测试多少东西是努力与回报的问题,所以这真的取决于个人项目。通常您会尝试遵循 80/20 规则,但在某些应用程序中您可能需要更多的测试覆盖率,因为失败会产生非常严重的后果。

      如果您使用测试驱动方法 (TDD),您可以大大减少编写测试所需的时间。那是因为没有考虑到可测试性编写的代码要困难得多,有时几乎无法测试。但由于生活中没有什么是免费的,因此使用 TDD 开发的代码本身往往更加复杂。

      【讨论】:

        【解决方案6】:

        我也开始了更一致地使用单元测试的过程,我发现单元测试中最大的任务是构建我的代码以支持测试。当我开始思考如何编写测试时,很明显类在哪里变得过度耦合,以至于“单元”的复杂性使得定义测试变得困难。我花在重构代码上的时间与编写测试一样多或更多。一旦可测试单元之间的界限变得更加清晰,从哪里开始测试的问题就会自行解决;从您最小的独立依赖项(或至少您担心的那些)开始,然后逐步向上。

        【讨论】:

          【解决方案7】:

          我测试了三个基本事件: 最小值、最大值以及介于最小值和最大值之间的某个位置。

          并且在适当的情况下有两个极端:低于最小值和高于最大值。

          有明显的例外(例如,某些代码可能没有最小值或最大值),但我发现对这些事件进行单元测试是一个好的开始,并且可以捕获代码的大多数“常见”问题。

          【讨论】:

            猜你喜欢
            • 2010-09-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-05-25
            • 2010-11-21
            • 1970-01-01
            • 2020-04-12
            相关资源
            最近更新 更多