我目前正在使用PHPUnit尝试和我正在编写的内容一起开发测试,但是,我正在编写会话管理器,并且遇到了这样的问题......

会话处理类的构造函数是

private function __construct()
{
    if (!headers_sent())
    {
        session_start();
        self::$session_id = session_id();
    }
}

但是,由于PHPUnit在开始测试之前发送文本,因此对此Object的任何测试都会返回失败的测试,因为HTTP“Headers”已发送......

有帮助吗?

解决方案

嗯,您的会话管理器基本上已经被设计破坏了。为了能够测试某些东西,必须能够将其与副作用隔离开来。不幸的是,PHP是以这样的方式设计的,它鼓励自由使用全局状态( echo header exit session_start 等。)。

您可以做的最好的事情是隔离组件中可以在运行时交换的副作用。这样,您的测试可以使用模拟对象,而实时代码使用具有真正副作用的适配器。你会发现这对单身人士来说效果不佳,我猜你正在使用它。因此,您必须使用其他一些机制来获取分配给代码的共享对象。您可以从静态注册表开始,但如果您不介意一点学习,还有更好的解决方案。

如果你不能这样做,你总是可以选择编写集成测试。例如。使用PHPUnit相当于 WebTestCase

其他提示

为phpunit创建一个引导程序文件,该文件调用:

session_start();

然后像这样启动phpunit:

phpunit --bootstrap pathToBootstrap.php --anotherSwitch /your/test/path/

引导程序文件在其他所有内容之前被调用,因此标题尚未发送,一切都应该正常工作。

phpUnit在测试运行时输出输出,因此即使在第一次测试中,headers_sent()也会返回true。

要解决整个测试套件的这个问题,您只需在设置脚本中使用ob_start()。

例如,假设您有一个名为AllTests.php的文件,它是phpUnit首先加载的文件。该脚本可能如下所示:

<?php

ob_start();

require_once 'YourFramework/AllTests.php';

class AllTests {
    public static function suite() {
        $suite = new PHPUnit_Framework_TestSuite('YourFramework');
        $suite->addTest(YourFramework_AllTests::suite());
        return $suite;
    }
}

我有同样的问题,我通过调用带有--stderr标志的phpunit就解决了这个问题:

phpunit --stderr /path/to/your/test

希望它有所帮助!

我认为“正确”解决方案是创建一个非常简单的类(这么简单,不需要测试),这是PHP的会话相关函数的包装,并使用它而不是直接调用 session_start()等。

在测试传递模拟对象中,而不是真正有状态的,不可测试的会话类。

private function __construct(SessionWrapper $wrapper)
{
   if (!$wrapper->headers_sent())
   {
      $wrapper->session_start();
      $this->session_id = $wrapper->session_id();
   }
}

我想知道为什么没有人列出XDebug选项:

/**
 * @runInSeparateProcess
 * @requires extension xdebug
 */
public function testGivenHeaderIsIncludedIntoResponse()
{
    $customHeaderName = 'foo';
    $customHeaderValue = 'bar';

    // Here execute the code which is supposed to set headers
    // ...

    $expectedHeader = $customHeaderName . ': ' . $customHeaderValue;
    $headers = xdebug_get_headers();

    $this->assertContains($expectedHeader, $headers);
}

在开始测试之前,你不能使用输出缓冲吗?如果缓冲输出的所有内容,则不应该设置任何标题,因为此时尚未向客户端发送任何输出。

即使在类中的某个地方使用OB,它也是可堆叠的,OB不应该影响内部发生的事情。

据我所知,Zend Framework为Zend_Session包测试使用相同的输出缓冲。您可以查看他们的测试用例以帮助您入门。

创建引导程序文件,指出4个帖子似乎是最简洁的方法。

通常使用PHP,我们必须维护,并尝试将某种工程规则添加到非常糟糕的遗留项目中。我们没有时间(或权威)抛弃整堆垃圾并重新开始,所以troelskn的第一个anwer并不总是可行的。 (如果我们可以回到最初的设计,那么我们可以完全放弃PHP并使用更现代的东西,比如ruby或python,而不是帮助延续Web开发世界的这个COBOL。)

如果您正在尝试为整个过程中使用session_start或setcookie的模块编写单元测试,那么在boostrap文件中启动会话可以解决这些问题。

因为我现在正在对我的引导程序进行单元测试(是的,我知道大多数人不这样做),我遇到了同样的问题(header()和session_start())。 我发现的解决方案相当简单,在您的unittest引导程序中定义一个常量,并在发送标题或启动会话之前简单地检查它:

// phpunit_bootstrap.php
define('UNITTEST_RUNNING', true);

// bootstrap.php (application bootstrap)
defined('UNITTEST_RUNNING') || define('UNITTEST_RUNNING', false);
.....
if(UNITTEST_RUNNING===false){
    session_start();
}

我同意这在设计上并不完美,但我正在对现有应用进行单元测试,不希望重写大部件。我也使用相同的逻辑来使用__call()和__set()魔术方法测试私有方法。

public function __set($name, $value){
    if(UNITTEST_RUNNING===true){
       $name='_' . $name;
       $this->$name=$value;
    }
    throw new Exception('__set() can only be used when unittesting!');
 }

您似乎需要注入会话才能测试代码。我使用的最佳选项是Aura.Auth,用于身份验证过程,并使用NullSession和NullSegment进行测试。

使用空会话进行光环测试

Aura框架编写精美,您可以单独使用Aura.Auth,而不需要任何其他Aura框架依赖项。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top