在PHPUnit中测试具有依赖项的对象

Dav*_*tom 9 php phpunit unit-testing qa

对于组成另一个对象作为其实现的一部分的对象,编写单元测试的最佳方法是什么,只有主要对象才会被测试?琐碎的例子:

class myObj { 
    public function doSomethingWhichIsLogged()
    {
        // ...
        $logger = new logger('/tmp/log.txt');
        $logger->info('some message');
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道可以设计对象以便可以注入logger对象依赖项并因此在单元测试中进行模拟,但情况并非总是如此 - 在更复杂的场景中,您需要组合其他对象或调用静态方法.

由于我们不想测试logger对象,只有myObj,我们如何进行?我们用测试脚本创建一个stubbed"double"吗?就像是:

class logger
{
    public function __construct($filepath) {}
    public function info($message) {}
}

class TestMyObj extends PHPUnit_Framework_TestCase 
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这对于小对象来说似乎是可行的,但对于更复杂的API而言,这将是一个痛苦,其中SUT依赖于返回值.另外,如果你想测试对依赖项对象的调用,你可以使用模拟对象吗?有没有办法模拟由SUT实例化的对象而不是传入?

我已经阅读了关于模拟的手册页,但它似乎并没有涵盖这种依赖是由组合而不是聚合的情况.你怎么做呢?

Ion*_*tan 6

跟随troelskn建议这是你应该做的一个基本的例子.

<?php

class MyObj
{
    /**
     * @var LoggerInterface
     */
    protected $_logger;

    public function doSomethingWhichIsLogged()
    {
        // ...
        $this->getLogger()->info('some message');
        // ...
    }

    public function setLogger(LoggerInterface $logger)
    {
        $this->_logger = $logger;
    }

    public function getLogger()
    {
        return $this->_logger;
    }
}


class MyObjText extends PHPUnit_Framework_TestCase
{
    /**
     * @var MyObj
     */
    protected $_myObj;

    public function setUp()
    {
        $this->_myObj = new MyObj;
    }

    public function testDoSomethingWhichIsLogged()
    {
        $mockedMethods = array('info');
        $mock = $this->getMock('LoggerInterface', $mockedMethods);
        $mock->expects($this->any())
             ->method('info')
             ->will($this->returnValue(null));

        $this->_myObj->setLogger($mock);

        // do your testing
    }
}
Run Code Online (Sandbox Code Playgroud)

有关模拟对象的更多信息,请参见手册.


tro*_*skn 4

正如您似乎已经意识到的那样,具体类依赖关系使测试变得困难(或完全不可能)。您需要解耦这种依赖关系。一个简单的更改不会破坏现有的 API,即默认为当前行为,但提供一个钩子来覆盖它。有多种方法可以实现这一点。

有些语言有可以将模拟类注入代码的工具,但我不知道 PHP 有这样的东西。在大多数情况下,无论如何重构代码可能会更好。