【问题标题】:Why are TDD tests methods so static?为什么 TDD 测试方法如此静态?
【发布时间】:2014-09-28 18:40:22
【问题描述】:

我看了几个关于测试驱动开发的视频,我基本了解了。但事实上,在我看过的所有视频中,这些方法都是静态的。所以在我的例子中,怪物必须总是有 100 点生命值,而攻击者有 70 点。那么当你只能覆盖一个案例时,这与测试有什么关系呢?

一个示例测试:

monster = Monster.new
monster.hitpoints = 100

hero = Hero.new
hero.strength = 70

hero.attack(monster)
monster.hitpoints.should == 30

这里是代码:

class Monster
    attr_accessor :hitpoints
end

class Hero
    attr_accessor :strength
    def attack(monster)
        monster.hitpoints -= strength
    end
end

【问题讨论】:

    标签: tdd


    【解决方案1】:

    测试方法是静态的,因为您正在观看视频。它们为您提供测试框架的概念和语法,不一定是“最佳”或“实际”实践。

    在您的示例中,您需要检查一些边缘情况,这些情况会导致看起来非常相似的测试。所以把那个通用代码分解出来。

    def test_post_attack_hitpoints(initial, strength, expected):
      monster = Monster.new
      monster.hitpoints = initial
    
      hero = Hero.new
      hero.strength = strength
    
      hero.attack(monster)
      monster.hitpoints.should == expected
    end
    
    test_post_attack_hitpoints(100, 70, 30)
    test_post_attack_hitpoints(100, 110, -10) # do you actually expect -10?
    test_post_attack_hitpoints(100, -10, 110) # can a hero's strength go negative? Should that heal the monster?
    

    一般来说,视频和教程会教你做某事的绝对最低限度。学会做好事情是一个经验问题。从学习其他人的经验开始。看看单元测试是如何在广受好评的开源项目中构建的。我不一定会指出 Ruby 世界中的好例子,但 Rails 的测试套件肯定已经被广泛使用,可能是一个合理的起点。看看他们是如何组织事物的。

    然后从你自己的经验中学习。写一堆单元测试。什么效果好,什么看起来很愚蠢?找到一种能够解决您自己(或您的团队)需求的风格,然后去做。

    我现在就离开我的肥皂盒。

    【讨论】:

      【解决方案2】:

      这样做完全是故意的。单元测试的一个基本特征是它们必须是可预测的,并且每次运行时都给出完全相同的结果。为什么?因为您希望代码完全可预测并且测试结果完全一致。每个运行测试的开发人员都应该得到完全相同的结果,无论何时、如何或在什么环境中,他们都可以以可预测的方式修复/重构/开发。

      每个测试(或应该)旨在测试一个特定情况或案例。在你的情况下,英雄攻击怪物会产生一定的伤害。您是对的,该特定测试仅涵盖一种情况,这样做是正确的。它正在测试设计的特定情况。这样做的好处是这些案例应该具有足够的代表性,可以涵盖大多数情况。

      例如,这是涵盖正常攻击情况的典型测试。例如,第二次测试将给英雄 50 的攻击,而怪物只有 30 点生命值,并且会断言怪物的生命值为 0 并死亡。还有一个可能会给怪物一个盾牌,并表示伤害已经减少。这个想法是创造有代表性的案例。一个不太有用的测试的示例是添加另一个具有英雄 50 攻击和怪物 200 命中的测试,本质上它在概念上与您的示例没有任何不同,并且很可能都通过或都失败。

      边缘情况也很有用。如果怪物的生命值和英雄的攻击相同,会发生什么?它应该杀死还是几乎死亡?另一个有趣的情况可能是无效的输入,它应该返回错误。

      但一般的想法是,它们是静态的,这是使它们可靠和可重复的一个特征。一项测试,一项特定的测试,仅此而已。

      也可以看看这里:What are the downsides using random values in Unit Testing?

      【讨论】:

        【解决方案3】:

        所有测试都是这样,不仅是 TDD 测试。根本问题是潜在测试输入的数量是巨大的。想象一下,您必须测试一个方法,该方法采用两个 32 位长的整数,并且您每秒可以编写和运行 1000 个测试用例。您需要多长时间来测试所有可能的输入?做一个猜测,然后进行计算。您可能会惊讶于它需要多长时间。相反,测试是有选择性的。我们选择了少量的测试用例,我们希望这些用例能够完成足够好的测试,但测试并不完美。

        测试技能的一部分是知道如何选择一组好的测试用例,尽管数量很少,但它们可以很好地完成测试。碰巧 TDD 可以很好地做到这一点,因为它坚持通过执行 minimum 来实现 green 阶段(通过所有测试),这将使测试通过。完成此操作后,您很可能会查看代码并发现它是不完整的。这将建议进一步的测试用例,这对于证明代码更接近完成是必要的。

        【讨论】:

          【解决方案4】:

          我将在已经很出色的答案上加一两分。

          如果您必须手动测试此方法,您是否必须检查所有值才能知道它是否有效?当然不是。您将测试“正常路径”和边缘情况,就像 neilh 在他的回答中所做的那样。这就是您编写自动单元测试(无论是否为 TDD)时所做的事情。

          不要将自动单元测试视为数学上详尽的测试。将它们视为在重构代码时保护您免于破坏任何东西的盾牌。让它们覆盖得足够多,以使它们获得您的完全信任(这是非常重要的一点),但不要试图覆盖应用程序的所有可能状态(您不能)。您需要测试所有可能的路径,这是非常不同的。

          【讨论】:

            猜你喜欢
            • 2012-07-20
            • 1970-01-01
            • 1970-01-01
            • 2020-11-27
            • 2015-08-04
            • 2020-07-13
            • 1970-01-01
            • 1970-01-01
            • 2015-05-26
            相关资源
            最近更新 更多