【问题标题】:unittest classes that use self:: in PHP在 PHP 中使用 self:: 的 unittest 类
【发布时间】:2012-06-29 09:08:33
【问题描述】:

您将如何测试通过self:: 具有附带影响的类的方法?如:

final class Foo{
    private static $effect = false;
    public static function doit( $arg ){
        if( self::effect ) return;
        self::check_args( $arg );
        self::$effect = true;
    }
    private function check_args($arg){
        #validate and trhow exception if invalid
    }
}

我必须向 doit() 发送几个参数来测试它是否正确地验证了值,但是在第一次运行之后它只是绕过它。它基本上是一个设置初始化标志的单例的初始化方法。

我现在还不能乱上课。有什么方法可以让我以与self:: 一起使用的方式复制/实例化对象?

【问题讨论】:

  • 这不是有效的语法。
  • 你试过 PHPUnit 中的 --static-backup 选项了吗?
  • @SamHennessy 似乎朝着正确的方向发展,但我未能找到更多相关信息。从phpunit.de/manual/3.6/en/textui.html 它将我重定向到phpunit.de/manual/3.6/en/fixtures.html#fixtures.global-state,它根本没有详细说明那个标志。我尝试使用标志运行测试,但它就像以前一样。
  • @Jon 我已将代码更新为正确,我的错。
  • @tereško 我很想了解这个项目的重构部分。

标签: php unit-testing testing phpunit


【解决方案1】:

该类应重写为:

class Foo{
    private $effect = false;
    public function doit( $arg ){
        if( $this->effect ) return;
        $this->check_args( $arg );
        $this->effect = true;
    }
    private function check_args($arg){
        #validate and trhow exception if invalid
    }
}

self:: 仅用于static 函数和变量,出于各种原因,在大多数情况下应避免使用。

【讨论】:

  • 我的错,我在输入更简单的类时忘记了“静态”关键字。我认为的问题更多是如何以某种方式重新初始化对象声明,以便在同一个 phpunit 运行中使用多个初始化参数对其进行测试。
  • @gbc 人们通常会告诉你,在大多数情况下使用静态是不好的做法。这与使用全局变量基本相同。这意味着根据全局变量的状态,相同的函数可能不会有相同的结果。有人可能会在您的代码的任何部分注入导致错误的东西,而您可能永远无法找到它。这有意义吗?在少数情况下可以放心使用它,例如对象注册表。众所周知,静态也很难进行单元测试。我建议学习依赖注入而不是使用全局静态。
  • 我同意这一切。但我被拖到了这个项目。我正在尝试为项目编写测试,然后推动重构。
  • @gbc 应用程序有多大?使用这样的代码,是否可以重写?可能是等量的工作。 PHPUnit 可以备份静态变量,然后将它们恢复到原来的状态。
  • @gcb 你可以在手册phpunit.de/manual/3.6/en/appendixes.annotations.html的注释下找到它
【解决方案2】:

如何在您的测试环境中添加被测类的子类,然后使用它来操作您的静态变量。在下面的示例中,我稍微修改了您的代码,以便 $effect 的值更易于使用,并且我还必须将 $effect 的范围扩大到受保护:

<?php
class Foo{
    protected static $effect = "0";
    public static function doit( $arg ) {
        echo 'I got: ' . $arg . ' and effect is: ' . self::$effect . '<br>';
        self::$effect = "1";
    }
}

class FooChild extends Foo {
    public static function setEffect($newEffect) {
        self::$effect = $newEffect;
    }
}

Foo::doit('hello');
Foo::doit('world');
FooChild::setEffect('3');
Foo::doit('three');

输出是这样的:

我得到:你好,效果是:0(第一次通过显示初始值)

我得到:世界和效果是:1(第二次显示在 doit() 中由 Foo 增加的值

我得到:三个,效果是:3(第三次通过,表明子类能够改变父类中的值)

【讨论】:

  • 好主意!这可能不是最好的纯单元测试(不仅您不只测试一种方法,而且您正在添加新方法)......但可能会解决更直接的问题。毕竟,我同意的主要问题在于原始代码。谢谢,我正在实施这一点,直到有人指出更好的模式(如果在这种情况下可能的话)
  • 嗯,不同之处在于子类将存在于您的测试世界中 - 而不是在生产代码中。所以我认为它是测试夹具的一部分。与您将在测试设置或拆卸中放入的代码没有什么不同。类似的策略是使用接缝,请参阅:stackoverflow.com/questions/7210851/… 但使用静态时,事情就有点棘手了。
猜你喜欢
  • 2019-02-17
  • 1970-01-01
  • 1970-01-01
  • 2021-01-06
  • 2020-05-31
  • 1970-01-01
  • 2023-03-28
  • 2017-12-01
  • 1970-01-01
相关资源
最近更新 更多