【问题标题】:Testable Code and global constants可测试代码和全局常量
【发布时间】:2011-05-12 07:20:47
【问题描述】:

我正在编写一个小应用程序,其唯一目的是获得更好的 OOP/可测试代码习惯。并且喜欢它,顺便说一句!

我正在努力吸收开发“可测试代码”背后的方法,主要是通过阅读 Sebastien Bergmann、Misko Hevery 和 Giorgio Sironi 等单元测试传播者的帖子。

我吸收的困难之一是滥用静态方法,依赖对象的对象依赖对象。目前,我被困在全球范围内的常数上。在我的应用程序开始时,我加载了一个 CONSTANT,它只是在调试或生产中设置应用程序模式:

/**
 *  APP_MODE values:
 *
 *  PROD   Production, no errors displayed email sent on error, logs to 
 *         logs/app-<date-time>.log.
 *
 *  DEBUG: All warnings and errors displayed, emails disabled and log messages 
 *         sent to console. Whether in-memory modifications to configuration 
 *         data are allowed
 */
 define("APPMODE", "DEBUG");

如何根据此常量的状态测试应用程序类以进行正确的错误处理?

起初我的想法是简单地将全局常量移动到类常量,而不是在我的 init 类中,这样就解决了这个特定类的情况,但我对这个过程并不满意。我的意思是,是否应该简单地避免在一个可能值always 的严格意义上不是“真正”常量的站点范围常量?

我无法想象测试人员必须为每个类编写 2 个测试套件,即 initClassDebugTest.php 和 initClassProdTest.php,除非 phpUnit 可以以某种方式重置全局状态?是否应该避免以这种方式使用全局常量?我有一种奇怪的直觉,我根本不应该在这里使用常量。我很想知道那里的测试精明编码器将如何在运行时处理具有 2 个可能值的全局定义。

【问题讨论】:

    标签: php unit-testing phpunit


    【解决方案1】:

    这主要取决于您如何创建对象以及有多少类访问此APPMODE

    让我们看看 APPMODE 做了什么:

     *  DEBUG: All warnings and errors displayed, emails disabled and log messages 
     *         sent to console. Whether in-memory modifications to configuration 
     *         data are allowed
    

    这样的问题通常可以通过将“DebugLogger”和“DontSendEmailMailer”传递给需要发送邮件的类来解决。

    通过这样做,您只需要几个需要了解“生产”与“开发”的工厂(或任何用于创建对象图的工厂)。

    执行实际业务逻辑的类不应该知道它是否在生产中运行。这意味着每个类的开发人员都必须关心这一点,如果你……说……有一个“登台”环境,每个类都需要改变。它引入了许多全局状态,就像您发现的那样,很难测试。

    是否应该在您的 php.ini 或应用程序引导程序中的模型中显示或不显示错误,并且不应该涉及应用程序的其余部分。

    我将开始将“调试”功能从需要您的 APPMODE 设置的类中移出,并将其移至专用(日志记录、邮件发送等)类中。真实的东西(实际发送邮件)和调试的东西(可能将邮件写入磁盘?)。这两个类都可以正确测试(测试空记录器非常容易;))并且您只需要进行几次切换。

    if($config->evironment() == "debug") {
        $logger = new DisplayEverythingLogger();
    } else {
        $logger = new OnlyLogErrorsToTextfileLogger();
    }
    
    
    $neededModel = new ClassThatDoesActualWork($logger);
    
    $controllerOrSomething = new ControllerOrWhatEveryDoesYourWorkflow($neededModel);
    $controllerOrSomething->dispatch();
    

    等等。您可以逐步减少全局状态的数量,直到摆脱定义并且只有配置设置。

    当谈到测试确实有效的类时,您现在赢得了以太网的方式,因为日志记录是可注入的,并且您可以传递一个模拟进行测试。


    到目前为止,第一个想法..如果您认为这对您不起作用,可以提供一个使用 APPMODE 的示例

    【讨论】:

    • 这是一个很好的开始。关键是,当测试一个应该在开发过程中记录错误的类时,而是根据APPMODE 设置向生产中的管理员发送电子邮件时,您应该只验证它是否调用了$this-&gt;errorReporter-&gt;logCatastrophicError()。该方法通常会使用APPMODE 来选择要做什么,但在测试期间,您会传入一个模拟以确保只调用它。请注意,APPMODE 并不是一个真正的常量,而是一个运行时配置选项。它不应该在定义或类常量中。
    【解决方案2】:

    使用 phpunit 的 --bootstrap 选项。它会在任何测试完成之前运行给定的文件。

    【讨论】:

    • 好的,所以可以两次运行测试套件,每次运行时使用不同的引导文件?并且根据 APP_MODE 的值对测试 error_handling 函数的断言有条件吗?
    • 我很可能错过了你的评论,如果我错过了,请解释一下......
    • @stefgosselin:全局常量 (APP_MODE) 将可用于您正在测试的代码。因此,您可以有两个单独的引导文件,一个用于调试,一个用于发布。对每个引导文件运行 phpunit 将为您提供调试和发布测试,而无需触及代码库的内部结构。
    猜你喜欢
    • 2013-01-14
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多