交换父类以进行单元测试

时间:2014-07-17 07:00:09

标签: php unit-testing mocking phpunit

我有以下情况。我们说我有两个班级:

class Session {
    public function start() {
        return session_start();
    }

    // methods for all the other session functions of PHP
}

扩展Session

的子类
class TrustedSession extends Session {
    public function start() {
        if(parent::start() === false)
            return false;

        $requestRemoteAddress = isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:null;
        $requestUserAgent = isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:null;

        $trustedRemoteAddress = $this->get('TRUSTED_REMOTE_ADDR');
        $prevUserAgent = $this->get('PREV_USERAGENT');

        if($trustedRemoteAddress !== $requestRemoteAddress || $prevUserAgent !== $requestUserAgent)
            $this->regenerateID(true);

        return true;
    }
}

所以我不想测试我的TrustedSession课程。但是我必须使用Session的模拟因为according to this question here我不能使用PHP的真实会话函数,因为它们会导致PHPUnit出现Output already started错误。

现在的问题是:如何模拟Session课程。我的意思是我可以创建一个SessionInterface来定义我必须为Session实现的方法及其模型。但是,如何使用模型扩展我的TrustedSession类?

我的想法是在运行时交换父类,但我认为这是不可能的(至少在PHP中)。这是一个设计问题,我正在思考上面描述的界面,但这并没有帮助我摆脱这种情况。有没有"清洁"和好的解决方案?

2 个答案:

答案 0 :(得分:0)

定义接口并使Session成为TrustedSession的依赖项:

interface ISession
{
    public function start();
}

class Session implements ISession
{
    public function start()
    {
        return session_start();
    }
}

class TrustedSession implements ISession
{
    private $session;

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

    public function start()
    {
        if (!$this->session->start()) {
            return false;
        }

        $requestRemoteAddress = isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:null;
        ....

        return true;

    }
}

这样,您可以在测试TrustedSession时注入Session Mock

答案 1 :(得分:-1)

我自己回答这个问题,因为当我使用PHPUnit与向客户端发送标头的PHP函数(如Output already started那样)进行测试时,我有办法解决这个问题session_start()

它非常简单,但也非常hacky,肯定是不洁净的。我所做的是使用phpunit.xml选项来执行引导程序文件。

<phpunit bootstrap="bootstrap.php" colors="true">
    <!-- testsuites here -->
</phpunit>

bootstrap.php文件中我放了这些代码行:

ob_start();

// Composer autoload file
require '../vendor/autoload.php';

现在,PHPUnit发送给客户端的所有输出都会被缓存,直到脚本结束。这适用于版本3.7.*中的PHPUnit。我还没有使用版本4.x对其进行测试,因为我使用PHPStorm进行开发。

这也仅适用于标准单元测试,并且由于代码覆盖模块中的某处被强制清理并发送给客户端而无法进行代码覆盖。我在bootstrap.php文件中开始的输出缓冲。