【问题标题】:Dealing with TDD / Unit Testing fatigue处理 TDD / 单元测试疲劳
【发布时间】:2010-08-08 03:55:53
【问题描述】:

所以我已经习惯了 TDD,但我遇到了一个意想不到的问题:我已经厌倦了 100% 的代码覆盖率。编写测试比编写代码本身更乏味,我不确定我是否做得对。我的问题是:你应该测试什么样的东西,什么样的东西是多余的?

例如,我有一个测试如下,我不确定它是否有用。我应该怎么做才能让我仍然遵循 TDD 但不会厌倦编写测试?

describe 'PluginClass'

    describe '.init(id, type, channels, version, additionalInfo, functionSource, isStub)'

        it 'should return a Plugin object with correct fields'

            // Create test sets
            var testSets = new TestSets()
            var pluginData = {
                'id'                : null,
                'type'              : null,
                'channels'          : null,
                'version'           : null,
                'additionalInfo'    : null,
                'functionSource'    : null,
                'isStub'            : true
            }
            testSets.addSet({ 'pluginData' : pluginData })
            var pluginData = {
                'id'                : "testPlugin1",
                'type'              : "scanner",
                'channels'          : ['channelA', 'channelB'],
                'version'           : "1.0",
                'additionalInfo'    : {'test' : "testing"},
                'functionSource'    : "function () {alert('hi')}",
                'isStub'            : false
            }
            testSets.addSet({ 'pluginData' : pluginData })

            for (var t = 0; t < testSets.getSets().length; t ++) {
                var aTestSet = testSets.getSet(t)

                var plugin = new Plugin().init( aTestSet.pluginData.id,
                                                aTestSet.pluginData.type,
                                                aTestSet.pluginData.channels,
                                                aTestSet.pluginData.version,
                                                aTestSet.pluginData.additionalInfo,
                                                aTestSet.pluginData.functionSource,
                                                aTestSet.pluginData.isStub  )

                plugin.getID().should.eql aTestSet.pluginData.id
                plugin.getType().should.eql aTestSet.pluginData.type
                plugin.getChannels().should.eql aTestSet.pluginData.channels
                plugin.getVersion().should.eql aTestSet.pluginData.version
                plugin.getAdditionalInfo().should.eql aTestSet.pluginData.additionalInfo
                eval("fn = " + aTestSet.pluginData.functionSource)
                JSON.stringify(plugin.getFunction()).should.eql JSON.stringify(fn)
                plugin.getIsStub().should.eql aTestSet.pluginData.isStub
            }

        end

    end

end

【问题讨论】:

  • IMO,这是一个验收测试。

标签: unit-testing tdd code-coverage


【解决方案1】:

当然,上面的“测试”在许多方面都过分了。它太长太复杂,难以阅读,并且断言太多东西。我很难想象这是如何从 TDD 过程中出现的。厌倦这样的事情并不奇怪......

测试驱动的开发意味着:你应该逐步进行,每一步都是一个单独的测试,只断言一件事,并且绝对不包含任何逻辑(即没有for、if/else 或类似的......) .所以上面的代码会产生大约 4-6 个单独的测试方法,然后你将一个一个地实现。首先断言正确的属性初始化(根据需要使用不同的值),然后确保方法按预期工作,依此类推......

代码覆盖率指标不会告诉您任何有关您的测试的信息,只是它可以向您显示任何测试都没有触及的生产代码。尤其是它不会告诉您是否真的测试了所触摸的代码(而不仅仅是触摸......)。这仅取决于您的测试质量。所以不要把代码覆盖率看得太严重,在很多情况下,用更好的测试来降低覆盖率是更可取的......

总之: 对几乎所有内容进行测试(100% 覆盖率)并不过分,但像您的示例中那样进行测试肯定是个问题。

我建议您回顾一下您的 TDD/单元测试实践,The Art Of Unit Testing 这本书可能是一个很好的资源...

HTH!
托马斯

【讨论】:

  • +1 推荐单元测试的艺术 - 这是一本提供非常好的建议的好书。
【解决方案2】:

我认为人们忘记的一件事是,通过自动化单元测试,它仍然是一种编码实践。设置模板/通用类、基类、辅助类或任何其他您熟悉的常规软件开发模式来进行单元测试是完全可以接受的。如果你觉得你一遍又一遍地做同样的事情,你可能是!这表明你的大脑正在告诉你:“有更好的方法”。

那就去吧。

【讨论】:

  • 非常正确。通常这表明某处有问题。
【解决方案3】:

单元测试的目标应该是测试代码中可能包含错误的部分。实现 100% 的测试覆盖率不应成为目标,AFAIK、TDD 并没有将其作为目标。

为不太可能包含重大错误的代码创建详尽的测试在现在和随着系统的发展都是一种乏味的浪费时间。 (在未来,重复的单元测试很可能会导致测试本身出现无意义的回归,从而浪费人们的时间来查找和修复。)

最后,在将某些开发方法应用于项目时,您和您的管理层应始终使用常识。从来没有一种方法可以完美地解决所有问题/项目。您的部分工作是发现该方法无法以最佳方式运行的情况……并在必要时进行调整,甚至放弃它。在这种情况下,您/您的项目使用 TDD 的方式让您发疯,这是一个明确的信号,表明有些事情是不对的。

【讨论】:

  • “100% 的测试覆盖率...... TDD 并未将其作为目标”。实际上,它以宗教形式 TDD 要求在任何一行实时代码之前编写测试。否则同意。
【解决方案4】:

您的测试试图同时验证太多东西。将其拆分为多个测试并重构您的测试代码(是的,允许使用辅助方法)。

另外,我觉得被测试的代码也做得太多了。解耦您的代码(通过使用提取类和提取方法等进行重构)并单独测试每段生产代码。你会发现这些测试会变得更少、更简单、更容易读写。

【讨论】:

    【解决方案5】:

    看来你是

    • 使用简单的成员分配测试构造函数。除非构造函数中有一些重要的逻辑,否则这是矫枉过正的。相反,依靠它来进行测试,作为将使用成员的其他一些测试的一部分。
    • 比较两个值对象之间的相等性。通过覆盖 equals 方法,重新定义/定义插件类型/类的相等检查。所以plugin.should.eql expected_plugin 应该是唯一的断言
    • 还可以考虑使用辅助对象来构造复杂的测试数据。

    【讨论】:

      【解决方案6】:

      疲劳、懒惰和冷漠是我们避免无意义工作的本能,例如编写琐碎的测试,这可能就是这种情况。尝试停止对琐碎的单元测试,您应该会立即感觉好些。

      您的特定测试远非复杂。我作为代码审查员参与了许多包含数千个单元和集成测试的项目,但还没有看到一个测试比实际的实时代码更容易阅读和理解的项目。 TDD 可读性、“作为文档的测试”和那种承诺根本不正确。这很容易证明——尝试通过只阅读测试或只阅读实时代码来学习新的代码库。哪种方式更全面? 正确设计的实时代码提供了组件的完整和整体图片,而测试只是关于它的零散断言。

      测试的唯一好处是自动化 - 一些额外的代码验证,您不必手动/编译器或代码分析无法完成。时期。 而这种好处需要付出高昂的成本 - 编写和维护测试,因此请谨慎选择要测试的内容。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-05
        • 2011-03-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多