当前位置: 首页 > 面试题库 >

TDD:为什么让应用程序代码知道它正在测试而不是运行可能是错误的?

邢嘉祯
2023-03-14
问题内容

在该线程中,Brian(唯一的回答者)说:“您的代码应以与测试无关的方式编写”

一条评论说:“您的代码绝对不应在全局“我正在测试的标志”上分支。”。

但是,两者都没有给出原因,我 真的
很想听听对此事的一些理性思考。进入给定的应用程序类别并设置一个布尔值以表示“这是一个测试,而不是运行”,这将非常容易(尤其是考虑到许多测试具有对应用程序类的程序包私有访问权限的事实)。

我发现自己跳了圈(注入了嘲笑的私有字段等)来实现的各种各样的事情可能变得更容易实现。

同样显而易见的是,如果您走得太远,可能会造成灾难性的后果……但是作为软件测试工具库中众多工具之一,为什么这个概念会遇到这种麻烦?

回答米克助记符:

一个简单的例子可能会有所帮助,如果您实际上是在方法中间创建一个新的类实例并将其分配给私有字段:在这种情况下,私有字段模拟将无济于事,因为您要替换私有字段领域。但是实际上创建一个真实的对象可能会非常昂贵:您可能需要在测试时将其替换为轻量级版本。

昨天我实际上遇到了这种情况,我的解决方案是创建一个名为createXXX()的新程序包私有方法…这样我就可以模拟它。但这反过来违背了这样的格言:“您不能创建仅适合您的测试的方法”!


问题答案:

我将这个答案分为两部分。首先,我将分享我对Brian答案的看法,然后我将分享一些有关如何有效测试的提示。

布赖恩答案的解释

Brian暗示了两个关键思想。我将分别针对每个人。

理念1:生产代码不应依赖于测试

您的代码应以与测试无关的方式编写。

生产的代码应该 依赖于测试。应该是相反的。

原因有很多:

  1. 更改测试 不会更改 代码 的行为
  2. 您的生产代码可以 独立 于测试代码进行编译和 部署
  3. 更新测试时, 无需重新编译 您的代码。
  4. 由于未运行测试代码而产生了意外的副作用,因此您的生产代码 可能不会失败

注意: 任何体面的编译器都会删除测试代码。尽管我认为这不是设计/测试系统不佳的借口。

想法2:您应该测试抽象而不是实现

无论您在什么环境中进行测试,都应尽可能接近真实世界。

听起来 像布莱恩可能在他的回答中这种想法在暗示。与最后一个想法不同,这个想法尚未得到普遍认可,因此请带上一粒盐。

通过测试抽象,您可以提高对测试单元的尊重程度。您同意 不会 与它的内部混为一谈并监视其内部状态。

为什么在测试期间我不应该监视对象的状态?

通过监视对象的内部,将导致以下问题:

  1. 您的测试将使 您与 单元 的特定实现联系在一起

例如,
是否要将类更改为使用其他排序算法?太糟糕了,因为您断言 必须 调用该quicksort函数,所以测试将失败。 __

  1. 您将 破坏封装

通过测试对象的内部状态,您将很容易放松该对象所具有的某些隐私。这将意味着您的更多 生产 代码也将提高对对象的可见性。

通过放松对象的封装,您正在诱使其他生产代码也依赖它。这不仅可以将您的 测试 绑定到特定的实现,而且还可以绑定整个系统本身。您 希望这种情况发生。

那我怎么知道这个班上课了?

测试所调用方法的前提条件和条件前提/结果。如果您需要更复杂的测试,请看我写的关于 模拟 和依赖注入的最后一节。

迷你笔记

只要* 您的生产代码与测试无关,我认为if (TEST_MODE)在main方法中添加一个并不一定很糟糕。 *

例如:

public class Startup {

    private static final boolean TEST_MODE = false;

    public static void main(String[] args) {
        if (TEST_MODE) {
            TestSuite testSuite = new TestSuite();
            testSuite.execute();
        } else {
            Main main = new Main();
            main.execute();
        }
    }
}

但是,如果您的其他类 知道 它们正在测试模式下运行,则将成为一个问题。如果您拥有if (TEST_MODE)所有生产代码,那么您就可以迎接上面提到的问题。

显然,在Java中,您可以使用诸如JUnit或TestNG之类的东西来代替它,但是我只是想分享我对该if (TEST_MODE)想法的看法。

如何有效测试

这是一个非常大的主题,因此我将在答案的这一部分中简短介绍。

  • 与其监视内部状态, 不如使用模拟和依赖注入

使用模拟,您可以断言您已注入的模拟方法已被调用。更好的是,依赖项注入将使您的类对所注入内容的实现的依赖关系反转。这意味着您可以交换事物的不同实现,而不必担心。

这完全消除了在类内部闲逛的需要。

如果我强烈推荐阅读一本书,那就是 Jeff Langr 撰写的《 具有测试驱动开发现代C ++编程》 。这可能是我用过的最好的TDD资源。
__

尽管标题中有C ++,但它的主要重点肯定是TDD。本书的开头讨论了这些示例应如何应用于所有(相似)语言。鲍勃叔叔甚至在前言中指出:

您需要成为C 程序员才能理解吗?当然不会。C
代码非常简洁,编写得井井有条,概念非常清晰,任何Java,C#,C甚至Ruby程序员都不会遇到麻烦。



 类似资料: