【问题标题】:PHPUnit: Stubbing multiple interfacesPHPUnit:存根多个接口
【发布时间】:2012-01-22 13:41:20
【问题描述】:

我开始掌握 PHPUnit,到目前为止发现它很容易使用,但我遇到了一个给我带来困难的测试用例。

我正在针对一组对象预期实现的接口(一些 PHP 接口,一些自制)编写代码,并且 SUT 需要一个输入对象来实现多个接口。例如:

class MyClass implements ArrayAccess, MyInterface
{
    // ...
}

SUT 执行以下操作:

class ClassToBeTested
{
    protected $obj = NULL;

    public function __construct ($obj)
    {
        $this -> obj = $obj;
    }

    public function methodToBeTested ()
    {
        if ($this -> obj instanceof ArrayAccess)
        && ($this -> obj instanceof MyInterface)
        {
            // ...
        }
    }

    public function otherMethodUnderTest ()
    {
        if ($this -> obj instanceof ArrayAccess)
        {
            // ...
        }
        else
        if ($this -> obj instanceof MyInterface)
        {
            // ...
        }
    }
}

我可以从一个接口或另一个接口创建一个存根,但我不知道您是否可以创建一个实现它们的存根。

protected function setUp ()
{
    $stubField  = $this -> getMockBuilder ('ArrayAccess')
            -> getMock ();
    $this -> object = new ClassToBeTested ($stubField);
}

protected function setUp ()
{
    $stubField  = $this -> getMockBuilder ('MyInterface')
            -> getMock ();
    $this -> object = new ClassToBeTested ($stubField);
}

是否可以从接口列表生成存根,或者我必须存根实现预期接口的具体类?这本身就造成了困难,因为需要存根的类本身需要另一个对象传递给它的构造函数,而且我似乎无法让 disableOriginalConstructor () 或 setConstructorArgs () 工作我认为这是因为有问题的具体类本身并不实现构造函数,而是从超类继承它。我在这里遗漏了一些明显的东西吗?

【问题讨论】:

    标签: php interface phpunit stub


    【解决方案1】:

    您有权编辑原始代码吗?如果是这样,我将创建一个扩展 ArrayAccess 和 MyInterface 的新接口。这样你应该能够存根/模拟一个对象来测试被测方法。

    【讨论】:

    • +1 因为这是一个非常明显的想法,我应该想到的。不过,在接受看看其他人的建议之前,我还是要让它打开一段时间。
    • 个人更喜欢@Maksim Kotlyar 的答案,因为在您的应用程序代码库中引入一个接口作为您的测试的存根将无用地污染您的生产代码。
    • @YohanG。就目前而言,可能是的。再看一遍(超过 4.5 年!)我认为 OP 的问题实际上是更深层次问题的症状。他传递给 CUT 的 $obj 可以实现 ArrayAccess 或 MyInterface。在我看来,还有另一个概念试图摆脱。他应该做的是在他的 CUT 中接受一个接口/类,并允许它抽象出两者之间的差异。因此摆脱那些可怕的 if...instanceof 块并变得更加多态。
    【解决方案2】:

    在应用程序代码库中创建一个界面来让测试愉快并不是一个好主意。我的意思是你可以创建这个接口,但如果你把它放在测试代码库的某个地方会更好。比如你可以直接把接口放在文件中测试用例类之后

    为了同时测试两个接口,我在测试用例文件中创建了一个接口(可以是其他任何地方)

    interface ApiAwareAction implements ActionInterface, ApiAwareInterface
    {
    }
    

    在我模拟了那个课程之后:

    $this->getMock('Payum\Tests\ApiAwareAction');
    

    【讨论】:

    • 这与创建另一个扩展两个接口的接口完全相同。但更糟糕。
    • 我的重点是将它放在测试命名空间中,与您将在其中使用它的测试用例相同的文件。您不应该将仅用于测试所需的接口放入应用程序代码中。从实现的角度来看,它当然可以是一个接口
    • 再一次,使用扩展这两个接口的新接口仍然是要走的路。您的抽象既不是实现(即使是测试),也不是接口。当你根据一个接口测试一个方法时,你根本不关心这个东西是如何实现的。因此,创建一个扩展两个接口的接口意味着“某些东西 [这里,模拟] 正在实现这个接口,它也实现了我需要的两个接口”。这条信息比“Something extends an abstract, which ask to implementation both interfaces”更清楚
    • “使用扩展这两个接口的新接口仍然是要走的路” 是的,我必须承认这更好,但我的主要观点是将这个接口放入测试命名空间。这就是我试图强调的。当然,只要你只在测试中使用它。
    • 接受的答案是正确的,但通过强调这样的接口应该只对您的测试可用,这更正确。
    【解决方案3】:

    如果将来有人碰巧看到这个答案,这在 PHPUnit 7 中对我有用:

    $mock = $this
      ->getMockBuilder([InterfaceA::class,InterfaceB::class])
      ->getMock();
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-04
    • 2015-08-23
    • 2011-11-10
    • 1970-01-01
    • 2018-11-15
    • 1970-01-01
    • 2018-02-08
    • 2011-01-03
    相关资源
    最近更新 更多