如何在PHP中单元测试if语句

时间:2017-07-13 20:23:31

标签: php unit-testing if-statement phpunit

我目前正在尝试使用if语句对get方法进行单元测试。我不知道如何测试这种方法,我找不到很多有用的信息。

/**
 * @var Info
 */
private $info = null;

# Not pertinent information skipped

public function getInfo()
{
    if(!$this->Info) {
        $this->Info = InfoFacade::getInfoByToken($this->getToken());
    }

    return $this->Info;
}

然后在InfoFacade类中有一个函数可以抓取一组信息并返回自己。

我的理解是我需要使用shouldreceive并且不应该接收,但我不确定是否应该使用dataProvider,如果是的话,如何正确应用它来处理这种情况。

我的问题是我如何测试这样的if语句,如果dataProvider是最让我相信的方式,我该如何正确设置dataProvider?

2 个答案:

答案 0 :(得分:1)

给出的答案在其介绍中是正确的,我想添加另一个方面,特别是在阅读下面有关于嘲笑的问题的评论之后。

  

您正在询问如何在公共方法中测试if语句。

对于这种情况,if语句实际上是公共方法getInfo()的内部细节很重要,单元测试不应该真正关心该实现细节。 实现细节是受测试对象(SUT)将在第一次调用(延迟加载)时获取信息数据并将其存储在内部以供将来参考。

这意味着你拥有的公共接口是一些实现细节的包装(该对象如何获取数据是对象的商业,但没有测试的业务)。

现在 - 在您编写时 - 进行单元测试时,您希望测试该方法中的所有路径。这需要两次调用该方法。在第一次调用中,您将获得数据,在第二次调用中,数据必须相同。所以从技术上讲,你需要做的就是两次调用该方法来测试内部结构。

$foo = new Foo();
$expected = $foo->getInfo();
$actual = $foo->getInfo();
$this->assertSame($expected, $actual);

然而,为了测试该单元,您实际上只需要调用该方法一次就知道它的工作方式,因为您可以预期私有财产不会因为它是私有的(正确封装的)而发生变化而您实际上没有&# 39;如果你使用那个对象,我想要关心。

所以实际上有人可能会说你不想测试if子句,因为这是一个实现细节。

注意:当您测量代码覆盖率时,您将看到已经覆盖了单个调用的所有行(尽管并非所有路径都已被单个调用覆盖)。这只是表明100%的覆盖率并不意味着你孤立地覆盖了所有路径。

我个人的理解是,对于单元测试来说,在测试中调用该方法一次就足够了,并断言它不会返回NULL。

$foo = new Foo();
$this->assertNotNull($foo->getInfo());

你可以通过更准确地断言你希望该方法返回的内容(例如某些具体类型而不仅仅是非NULL)来使这更严格,但对于单元测试,这应该已经足够了。

关键在于,您首先要测试SUT的公共接口。如果存在具有特定实现的回归,您只需要测试详细信息。这属于另一个测试,所以你可以在一年后删除它,b / c测试只需要修复回归,经过一段时间后根本不再需要

然后更好:对于正在使用的Facade,您可能想要测试Facade是否正常工作,即调用该全局函数并检查预期结果。这是对Facade的另一个测试,而不是与它合作的对象。

否则你可以为了它而嘲笑事物,但是在测试中保持简单是比较好的,因为在编程中通常就是这样。否则,总是倾向于过度设计事物,这可能会对测试造成伤害。你会转向公共界面 ad absurdum ,但你想要依赖它们。因此,要相信依赖它们就是那种考验。

作为补充说明,请考虑即使方法的实现细节发生更改,您的测试也会测试。这可能是一个很好的描述,您希望随着时间的推移改变一些事情,如果更改没有改变任何(由单元测试指定)的意图,现有的测试应该向您报告。

另一个注意事项:当你正在使用initialize-property-if-null-on-get时,你也可以用PHP内联它:

public function getInfo()
{
    return $this->Info 
        ?: $this->Info = InfoFacade::getInfoByToken($this->getToken());
}

或:

public function getInfo()
{
    return $this->Info 
        ?? $this->Info = InfoFacade::getInfoByToken($this->getToken());
}

请参阅 = 三元(第一个示例)或 null-coalesce 运算符(第二个示例)(请参阅所有这些的PHP手册)。对于最后两个的比较,请参阅

答案 1 :(得分:0)

单元测试的想法是在测试之前有一个已知状态,在测试之后有一个预期状态。在这里,您希望仅在第一次调用getInfo时更改状态。

要运行此测试,您将从Infonull的状态开始。然后,您可以调用该方法并检查返回的信息是否是您希望由Facade返回的信息。然后更改外观中的内容并再次调用getInfo。返回的内容应与第一次调用时相同,而不是更改外观的内容。