29

假设我有一个具有私有属性和关联的公共 getter 和 setter 的类。我想用 PHPUnit 测试该属性在使用 setter 后是否获得正确的值,或者 getter 返回正确的属性。

当然,我可以通过使用 getter 来测试 setter,以查看对象是否存储了正确的值,反之亦然以测试 getter。但是,这并不能保证私有属性就是正在设置的属性。

假设我有以下课程。我创建了一个属性,getter 和 setter。但是我在属性名称中打错了字,所以 getter 和 setter 实际上并没有操纵它们要操纵的属性

class SomeClass
{
    private 
        $mane = NULL; // Was supposed to be $name but got fat-fingered!

    public function getName ()
    {
        return ($this -> name);
    }

    public function setName ($newName)
    {
        $this -> name = $newName;
        return ($this);
    }
}

如果我运行以下测试

public function testSetName ()
{
    $this -> object -> setName ('Gerald');
    $this -> assertTrue ($this -> object -> getName () == 'Gerald');
}

我会得到一个通行证。但是,实际上发生了我不希望的非常糟糕的事情。当调用 setName() 时,它实际上在类中创建了一个新属性,其名称我认为我的私有属性具有,只有 setter 创建的那个是公共的!我可以用以下代码证明这一点:

$a  = new SomeClass;

$a -> setName('gerald');
var_dump ($a -> getName ());
var_dump ($a -> name);

它会输出:

字符串(6)“杰拉德”

字符串(6)“杰拉德”

有什么方法可以从 PHPUnit 访问私有属性,这样我就可以编写测试来确保我认为正在获取和设置的属性实际上正在被获取和设置?

或者我应该在测试中做些什么来捕捉这样的问题而不试图访问被测对象的私有状态?

4

4 回答 4

42

您也可以使用Assert::assertAttributeEquals('value', 'propertyName', $object).

https://github.com/sebastianbergmann/phpunit/blob/3.7/PHPUnit/Framework/Assert.php#L490

于 2013-10-03T13:08:21.340 回答
25

对于测试属性,我会提出与测试私有方法相同的论点。

You usually don't want to do this.

这是关于测试可观察的行为。

如果您重命名所有属性或决定将它们存储到数组中,则根本不需要调整测试。您希望您的测试告诉您一切仍然有效!当您需要更改测试以确保一切仍然有效时,您将失去所有好处,因为您也可能在更改测试时出错。

所以,总而言之,你失去了测试套件的价值!


只测试 get/set 组合就足够了,但通常不是每个 setter 都应该有一个 getter,仅仅创建它们进行测试并不是一件好事。

通常,你设置一些东西,然后告诉方法DO(行为)一些东西。对此进行测试(该类执行应执行的操作)是测试的最佳选择,并且应该使测试属性变得多余。


如果您真的想这样做,那么setAccessiblePHP 反射 API 中有该功能,但我无法举出一个我认为这是可取的示例

寻找未使用的属性来捕捉像这样的错误/问题:

PHP Mess Detector作为一个UnusedPrivateField Rule

class Something
{
    private static $FOO = 2; // Unused
    private $i = 5; // Unused
    private $j = 6;
    public function addOne()
    {
        return $this->j++;
    }
}

这将为您生成两个警告,因为永远不会访问变量

于 2012-01-19T16:24:25.337 回答
3

我只想指出一件事。让我们暂时忘记私有字段,专注于您班级的客户关心的内容。在这种情况下,您的类公开了一个合同 - 更改和检索名称的能力(通过 getter 和 setter)。预期的功能很简单:

  • setName当我用to设置名称时"Gerald",我希望"Gerald"在我打电话时得到getName

就这样。客户不会(好吧,不应该!)关心内部实现。无论您是使用私有字段名称、哈希集还是通过动态生成的代码调用 Web 服务 - 对客户端而言都无关紧要。从用户的角度来看,您当前遇到的错误根本不是错误。

PHPUnit 是否允许您测试私有变量 - 我不知道。但是从单元测试的角度来看,你不应该那样做。

编辑(回应评论):

我理解您对可能暴露内部状态的担忧,但是我不认为单元测试是解决这个问题的正确工具。你可以想出很多可能的场景,比如某件事可能会做一些没有计划的事情。单元测试绝不是万能的,不应该这样使用。

于 2012-01-19T16:06:43.160 回答
2

我同意其他人的观点,一般来说,您希望避免在测试中访问 privates,但是对于您需要的情况,您可以使用反射来读取和写入 property

于 2012-01-19T19:12:38.393 回答