2008-10-10 9 views
47

Actualmente estoy trabajando con PHPUnit para intentar desarrollar pruebas junto con lo que estoy escribiendo, sin embargo, actualmente estoy trabajando en la escritura del Administrador de sesiones, y tengo problemas para hacerlo. ...Pruebas unitarias con elementos que necesitan enviar encabezados

el constructor de la clase de manejo de sesión es

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

Sin embargo, como PHPUnit envía el texto antes de que comience la prueba, cualquier prueba en este objeto devuelve una prueba fallida, como el HTTP "encabezados "se han enviado ...

+0

Acabo de encontrarme con el mismo problema, pero ninguna de las respuestas a continuación funciona. ¿Qué hiciste al final? Por favor envíame un correo electrónico. –

Respuesta

36

Bueno, su administrador de sesión está roto por diseño. Para poder probar algo, debe ser posible aislarlo de los efectos secundarios. Desafortunadamente, PHP está diseñado de tal manera que fomenta el uso liberal del estado global (echo, header, exit, session_start etc., etc.).

Lo mejor que puede hacer es aislar los efectos secundarios en un componente, que pueden intercambiarse en tiempo de ejecución. De esta forma, sus pruebas pueden usar objetos simulados, mientras que el código directo usa adaptadores que tienen efectos secundarios reales. Descubrirás que esto no funciona bien con los singleton, que presumo que estás usando. Por lo tanto, deberá usar algún otro mecanismo para distribuir objetos compartidos a su código. Puede comenzar con un registro estático, pero hay soluciones aún mejores si no le importa un poco de aprendizaje.

Si no puede hacerlo, siempre tiene la opción de escribir pruebas de integración. P.ej. use el equivalente de PHPUnit de WebTestCase.

+0

¡Gracias, necesitaba este amor duro! Mi diseño es indudablemente mejor ahora que he aislado los efectos secundarios ambientales del resto de mi clase. – DaveGauer

+1

La documentación para WebTestCase se encuentra ahora en http://www.simpletest.org/en/web_tester_documentation.html. – untill

0

No se puede utilizar la salida b antes de comenzar la prueba? Si almacenas en un búfer todo lo que se emite, no deberías tener problemas para configurar ningún encabezado, ya que hasta ese momento no se habría enviado ninguna salida al cliente.

Incluso si OB se utiliza en algún lugar dentro de sus clases, es apilable y el OB no debe afectar lo que está sucediendo dentro.

+0

parece que ob_start() en realidad no restablece si PHP piensa que los encabezados se envían. – Mez

+0

Sí, ob_start no funciona con los encabezados, por lo que incluso si lo usa, seguirá quejándose si ya no se pueden enviar los encabezados. Podría editar PHPUnit para esto ... – Mez

0

Por lo que sé, Zend Framework usa el mismo buffer de salida para sus pruebas del paquete Zend_Session. Puede echar un vistazo a sus casos de prueba para comenzar.

5

Creo que la solución "correcta" es crear una clase muy simple (tan simple que no necesita ser probada) que es un envoltorio para las funciones relacionadas con la sesión de PHP, y usarlo en lugar de llamar al session_start(), etc. directamente.

En el objeto de simulación de pase de prueba en lugar de una clase de sesión con estado real y no comprobable.

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

Crear un archivo de arranque para PHPUnit, que llama:

session_start(); 

Entonces comienzan PHPUnit así:

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

El archivo de arranque es llamado antes de todo lo demás, por lo que la cabecera hasn Se han enviado y todo debería funcionar bien.

+1

Si está utilizando un archivo de configuración XML, puede configurar el bootstrap agregando un atributo bootstrap a su elemento raíz ' 'de la siguiente manera:' ... ' –

0

La creación del archivo bootstrap, señaló 4 publicaciones atrás parece la manera más limpia de evitar esto.

A menudo, con PHP tenemos que mantener e intentar agregar algún tipo de disciplina de ingeniería a los proyectos heredados que se construyen abismalmente.No tenemos el tiempo (o la autoridad) para deshacernos de todo el montón de basura y comenzar de nuevo, por lo que la primera respuesta de troelskn no siempre es posible como una forma de avanzar. (Si pudiéramos volver al diseño inicial, podríamos descartar PHP por completo y usar algo más moderno, como ruby ​​o python, en lugar de ayudar a perpetuar este COBOL del mundo del desarrollo web.)

Si usted es tratando de escribir pruebas unitarias para los módulos que usan session_start o setcookie en todos ellos, que comenzar la sesión en un archivo boostrap soluciona estos problemas.

19

phpUnit imprime la salida a medida que se ejecutan las pruebas, lo que hace que headers_sent() devuelva verdadero incluso en su primera prueba.

Para solucionar este problema en un conjunto de pruebas completo, simplemente necesita usar ob_start() en su script de configuración.

Por ejemplo, supongamos que tiene un archivo llamado AllTests.php que es lo primero que carga phpUnit. Ese guión podría ser similar al siguiente:

<?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; 
    } 
} 
+2

La mejor solución para probar el código PHP de la caja negra que no puede cambiar. – Ando

+0

Tenga en cuenta que PHP 3.6 ahora hace esto automáticamente. –

+2

Esta solución funcionó para mí: D PHPUnit 3.6 se supone que debe implementar esto, pero no funcionó correctamente para mí. Agregué ob_start(); en mi archivo bootstrap.php y todas mis pruebas con cookies y encabezados comenzaron a funcionar correctamente nuevamente. – Tim

0

Como estoy prueba unitaria mi rutina de carga en este momento (sí sé que la mayoría de ustedes no hacen eso), estoy corriendo en el mismo problema (tanto la cabecera() y session_start()). La solución que he encontrado es bastante simple, en su unittest Bootstrap definir una constante y simplemente comprobar que antes de enviar el encabezado o iniciar la sesión:

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

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

Estoy de acuerdo que esto no es perfecto por diseño, pero estoy Al probar una aplicación existente, no se desea reescribir partes grandes. También uso la misma lógica para probar métodos privados usando los métodos mágicos __call() y __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!'); 
} 
11

que tenían el mismo problema y lo resolvió llamando PHPUnit con --stderr bandera como este:

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

espero que ayude a alguien!

+0

Después de una o dos horas de búsqueda, esta fue la solución más fácil y mejor para permitirme probar el código que tenía setcookie() enterrado dentro de él. Usar @runInSeparateProcess causa otros problemas mientras que esto es perfecto (por ahora). – Frug

1

Me pregunto opción XDebug por qué nadie ha enumerado:

/** 
* @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); 
} 
0

Parece que necesita inyectar la sesión para que pueda probar el código. La mejor opción que he usado es Aura.Auth para el proceso de autenticación y usando NullSession y NullSegment para probar.

Aura testing with null sessions

El marco Aura está muy bien escrita y se puede utilizar Aura.Auth por sí sola sin ningún otro dependencias marco Aura.