首页 文章

单元测试的新手,如何编写出色的测试? [关闭]

提问于
浏览
201

我对单元测试世界还不熟悉,我本周决定为现有应用添加测试覆盖率 .

这是一项艰巨的任务,主要是因为要测试的类数量,还因为编写测试对我来说都是新的 .

我已经为一堆课程编写了测试,但现在我想知道我是否做得对 .

当我为一个方法编写测试时,我感觉第二次重写我已经在方法本身中编写的内容 .
我的测试似乎与方法紧密相关(测试所有代码路径,期望一些内部方法被调用多次,带有某些参数),似乎如果我重构该方法,即使测试也会失败该方法的最终行为没有改变 .

这只是一种感觉,如前所述,我没有测试经验 . 如果一些更有经验的测试人员可以给我建议如何为现有应用程序编写出色的测试,那将非常感激 .

编辑:我要感谢Stack Overflow,我在不到15分钟的时间内获得了很多投入,回答了我刚刚在网上阅读的更多时间 .

7 回答

  • 17

    单元测试是关于从函数/方法/应用程序获得的输出 . 结果如何产生并不重要,重要的是它是正确的 . 因此,您计算内部方法调用的方法是错误的 . 我倾向于做的是坐下来写一个方法应该返回给定某些输入值或某个环境,然后编写一个测试,将返回的实际值与我想出的值进行比较 .

  • 10

    对于单元测试,我发现测试驱动(测试第一,第二代码)和代码优先,第二测试非常有用 .

    而不是编写代码,然后编写测试 . 编写代码然后看看你认为代码应该做什么 . 考虑它的所有预期用途,然后为每个用途编写测试 . 我发现编写测试比编码本身更快但更复杂 . 测试应测试意图 . 还考虑了在测试编写阶段找到极端情况的意图 . 当然,在编写测试时,您可能会发现少数几种用法之一会导致错误(我经常发现这种错误,而且我很高兴这个错误不会破坏数据并且不会被检查) .

    然而,测试几乎就像编码两次 . 事实上,我的应用程序中存在比应用程序代码更多的测试代码(数量) . 一个例子是一个非常复杂的状态机 . 我必须确保在添加更多逻辑之后,整个事情总是适用于所有以前的用例 . 由于这些案例很难通过查看代码来完成,我最终为这台机器配备了这么好的测试套件,我确信它在进行更改后不会破坏,并且测试节省了我的屁股几次 . 并且当用户或测试人员发现错误的流量或角落案件下落不明时,猜猜是什么,添加到测试中并且从未再次发生过 . 除了让整个东西超级稳定之外,这确实让用户对我的工作充满信心 . 当出于性能原因而不得不重新编写时,猜测一下,由于测试,它在所有输入上都按预期工作 .

    function square(number) 这样的所有简单例子都非常棒,并且可能是花费大量时间进行测试的不良候选者 . 那些做重要业务逻辑的人,那就是测试很重要的地方 . 测试要求 . 不要只是测试管道 . 如果要求改变然后猜测什么,测试也必须 .

    测试不应该字面上测试函数foo调用函数栏3次 . 那是错的 . 检查结果和副作用是否正确,而不是内部机制 .

  • 26

    我的测试似乎与方法紧密相关(测试所有代码路径,期望一些内部方法被调用多次,带有某些参数),似乎如果我重构该方法,即使重新测试,测试也会失败方法的最终行为没有改变 .

    我觉得你做错了 .

    单元测试应该:

    • 测试一种方法

    • 为该方法提供了一些特定的参数

    • 测试结果是否符合预期

    它不应该查看方法内部以查看它正在做什么,因此更改内部不应导致测试失败 . 您不应该直接测试正在调用的私有方法 . 如果您有兴趣了解您的私人代码是否正在测试,那么请使用代码覆盖工具 . 但不要为此着迷:100%的覆盖范围不是必需的 .

    如果您的方法在其他类中调用公共方法,并且这些调用由您的接口保证,那么您可以使用模拟框架测试这些调用是否正在进行 .

    您不应该使用方法本身(或它使用的任何内部代码)动态生成预期结果 . 预期的结果应该硬编码到您的测试用例中,以便它在何时不会改变实施变化 . 以下是单元测试应该做的简化示例:

    testAdd()
    {
        int x = 5;
        int y = -2;
        int expectedResult = 3;
    
        Calculator calculator = new Calculator();
        int actualResult = calculator.Add(x, y);
        Assert.AreEqual(expectedResult, actualResult);
    }
    

    请注意,不检查结果的计算方式 - 只检查结果是否正确 . 继续添加越来越多的简单测试用例,直到您已经涵盖尽可能多的方案为止 . 使用代码覆盖率工具查看您是否错过了任何有趣的路径 .

  • 13

    在编写要测试的方法之前,尝试编写单元测试 .

    这肯定会迫使你对事情的完成方式略有不同 . 您将不知道该方法将如何工作,只是它应该做什么 .

    您应始终测试方法的结果,而不是方法如何获得这些结果 .

  • 3

    测试应该提高可维护性 . 如果您更改方法并且测试中断可能是一件好事 . 另一方面,如果你把你的方法看成一个黑盒子,那么它应该把这个方法视为一个黑盒子 . 你可以做的唯一事情是进行集成测试 - 你加载一个完全实例化的被测服务实例,并让它像你在应用程序中运行一样 . 然后你可以把它当作一个黑盒子 .

    When I'm writing tests for a method, I have the feeling of rewriting a second time what I          
    already wrote in the method itself.
    My tests just seems so tightly bound to the method (testing all codepath, expecting some    
    inner methods to be called a number of times, with certain arguments), that it seems that
    if I ever refactor the method, the tests will fail even if the final behavior of the   
    method did not change.
    

    这是因为您在编写代码后编写测试 . 如果你反过来做(先写下测试)就不会有这种感觉 .

  • 145

    值得注意的是,对现有代码进行改编的单元测试比首先通过测试驱动创建代码要困难得多 . 这是处理遗留应用程序的一个重要问题......如何进行单元测试?之前已经多次询问过这个问题(所以你可能会因为欺骗问题而被关闭),人们通常会在这里结束:

    Moving existing code to Test Driven Development

    我接受了接受的答案的书籍推荐,但除此之外,答案中还有更多的信息 .

  • 5

    不要编写测试来全面覆盖您的代码 . 编写可以保证您的要求的测试 . 您可能会发现不必要的代码路径 . 相反,如果它们是必要的,它们就是满足某种要求的;找到它是什么并测试要求(而不是路径) .

    保持测试小:每个要求一次测试 .

    之后,当您需要进行更改(或编写新代码)时,请先尝试编写一个测试 . 只有一个 . 然后,您将迈出测试驱动开发的第一步 .

相关问题