php 使用需要发送标头的项目进行单元测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/190292/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 21:52:09  来源:igfitidea点击:

Unit Testing with items that need to send headers

phpunit-testingphpunit

提问by Mez

I'm currently working with PHPUnit to try and develop tests alongside what I'm writing, however, I'm currently working on writing the Session Manager, and am having issues doing so...

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

The constructor for the Session handling class is

会话处理类的构造函数是

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

However, as PHPUnit sends out text before it starts the testing, any testing on this Object returns a failed test, as the HTTP "Headers" have been sent...

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

采纳答案by troelskn

Well, your session manager is basically broken by design. To be able to test something, it must be possible to isolate it from side effects. Unfortunately, PHP is designed in such a way, that it encourages liberal use of global state (echo, header, exit, session_startetc. etc.).

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

The best thing you can do, is to isolate the side-effects in a component, that can be swapped at runtime. That way, your tests can use mocked objects, while the live code uses adapters, that have real side-effects. You'll find that this doesn't play well with singletons, which I presume you're using. So you'll have to use some other mechanism for getting shared objects distributed to your code. You can start with a static registry, but there are even better solutions if you don't mind a bit of learning.

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

If you can't do that, you always have the option of writing integration-tests. Eg. use the PHPUnit's equivalent of WebTestCase.

如果你不能这样做,你总是可以选择编写集成测试。例如。使用 PHPUnit 的等价物WebTestCase

回答by Dominik

Create a bootstrap file for phpunit, which calls:

为 phpunit 创建一个引导文件,它调用:

session_start();

Then start phpunit like this:

然后像这样启动phpunit:

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

The bootstrap file gets called before everything else, so the header hasn't been sent and everything should work fine.

bootstrap 文件在其他一切之前被调用,所以标头还没有发送,一切都应该正常工作。

回答by Michael

phpUnit prints output as the tests run thus causing headers_sent() to return true even in your first test.

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

To overcome this issue for an entire test suite you simply need to use ob_start() in your setup script.

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

For example, say you have a file named AllTests.php that is the first thing loaded by phpUnit. That script might look like the following:

例如,假设您有一个名为 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;
    }
}

回答by Juanjo Lainez Reche

I had the same issue and I solved it by calling phpunit with --stderr flag just like this:

我遇到了同样的问题,我通过使用 --stderr 标志调用 phpunit 来解决它,就像这样:

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

Hope it helps someone!

希望它可以帮助某人!

回答by Kornel

I think the "right" solution is to create a very simple class (so simple it doesn't need to be tested) that's a wrapper for PHP's session-related functions, and use it instead of calling session_start(), etc. directly.

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

In the test pass mock object instead of a real stateful, untestable session class.

在测试中通过模拟对象而不是真正的有状态、不可测试的会话类。

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

回答by user487772

I'm wondering why nobody have listed XDebug option:

我想知道为什么没有人列出 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);
}

回答by lost in binary

It seems that you need to inject the session so that you can test your code. The best option I have used is Aura.Auth for the authentication process and using NullSession and NullSegment for testing.

似乎您需要注入会话以便您可以测试您的代码。我用过的最好的选择是 Aura.Auth 用于身份验证过程并使用 NullSession 和 NullSegment 进行测试。

Aura testing with null sessions

使用空会话进行 Aura 测试

The Aura framework is beautifully written and you can use Aura.Auth on its own without any other Aura framework dependencies.

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

回答by pilsetnieks

Can't you use output buffering before starting the test? If you buffer everything that is output, you shouldn't have problems setting any headers as no output would have yet been sent to client at that point.

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

Even if OB is used somewhere inside your classes, it is stackable and OB shouldn't affect what's going on inside.

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

回答by pilsetnieks

As far as I know Zend Framework uses the same output buffering for their Zend_Session package tests. You can take a look at their test cases to get you started.

据我所知,Zend Framework 对其 Zend_Session 包测试使用相同的输出缓冲。您可以查看他们的测试用例以开始使用。

回答by Flip Vernooij

As I'm unittesting my bootstrap right now (yes I know most of you don't do that), I'm running in to the same problem (both header() and session_start()). The solution I found is rather simple, in your unittest bootstrap define a constant and simply check it before sending the header or starting the session:

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

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

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

I agree that this is not perfect by design, but I'm unittesting an existing application, rewriting large parts is not desired. I also using the same logic to test private methods using the __call() and __set() magic methods.

我同意这在设计上并不完美,但我正在对现有应用程序进行单元测试,不需要重写大型部分。我还使用相同的逻辑来测试使用 __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!');
 }