2011-07-25 3 views
6

Esto no es tanto una pregunta como un intento de salvar a alguien más la hora que acabo de desperdiciar en PHPUnit.¿Qué sucede con los objetos pasados ​​entre las pruebas PHPUnit dependientes?

Mi problema era que mi objeto simulado, cuando se usaba en una prueba dependiente, no devolvía el valor esperado. Parece que PHPUnit no conserva el mismo objeto entre las pruebas dependientes, a pesar de que la sintaxis hace que parezca.

¿Alguien sabe por qué PHPUnit hace esto? ¿Es esto un error? Cosas como esta en PHPUnit lo hacen muy frustrante de usar.

<?php 
class PhpUnitTest 
extends PHPUnit_Framework_TestCase 
{ 
private $mock; 

public function setUp() 
{ 
    $this->mock = $this->getMock('stdClass', array('getFoo')); 

    $this->mock->expects($this->any()) 
     ->method('getFoo') 
     ->will($this->returnValue('foo')); 
} 

public function testMockReturnValueTwice() 
{ 
    $this->assertEquals('foo', $this->mock->getFoo()); 
    $this->assertEquals('foo', $this->mock->getFoo()); 

    return $this->mock; 
} 

/** 
* @depends testMockReturnValueTwice 
*/ 
public function testMockReturnValueInDependentTest($mock) 
{ 
    /* I would expect this next line to work, but it doesn't! */ 
    //$this->assertEquals('foo', $mock->getFoo()); 

    /* Instead, the $mock parameter is not the same object as 
    * generated by the previous test! */ 
    $this->assertNull($mock->getFoo()); 
} 

} 
+0

Agregue la línea de comandos de cómo invocó phpunit a medida que se encuentra con el problema. - ¿y hay alguna razón por la que haces que '$ mock' sea un miembro privado? – hakre

+1

AFAIK phpunit ejecuta el método setUp() antes de cada prueba, por lo que restablece el valor de $ this-> mock –

+0

Hubiera esperado que esto funcionara como lo escribiste, pensé que 'setUp()' no sería necesario @dependant tests así que realmente me sorprendió que eso también fallara ... Eso me podría haber MORDIDO si hubiera tenido un simulacro pasado a una clase que paso con @depends :) – edorian

Respuesta

5

Los objetos falsos en PHPUnit se adjuntan a la instancia de prueba para la que se crearon, y esto por definición significa un único método de prueba. La razón de esto es que PHPUnit le permite especificar expectativas en un simulacro que deben cumplirse durante una prueba. Para hacer esto, confirma esas expectativas una vez que el método termina exitosamente. Si el simulacro sobrevivió a las pruebas, las expectativas no funcionarían.

El problema es que esto no es compatible con objetos stub: simulacros que contienen solo acciones enlazadas que deben tomarse en respuesta a los métodos y las entradas. Los stubs no validan que se invoquen sus métodos como pueden hacerlo los simulacros completos. Quizás PHPUnit podría beneficiarse de la capacidad de crear stubs en setUpBeforeClass() que no están vinculados a la instancia de prueba.

Su otra opción es utilizar una biblioteca externa de objetos falsos como Mockery o Phake.

Editar: Después de revisar el código de muestra nuevamente, me pregunto por qué está sorprendido con este comportamiento. Como escribió Shaunak, se llama al setUp() en una instancia nueva antes de que se ejecute cada método de prueba. Por lo tanto, cada instancia recibe un nuevo simulacro stdClass. Si desea que solo un método de prueba reciba una expectativa, agréguela dentro del método de prueba. Aún puede crear el objeto simulado en setUp() con cualquier comportamiento que sea común a todos los métodos de prueba.

+0

Por lo tanto, para reformular: ¿Después de cada prueba, todos los objetos simulados que se crearon en esa prueba se eliminan de todas sus 'expect'ations asignadas? – edorian

+0

@David: La razón por la que estoy confundido es que la sintaxis hace que parezca que el valor de retorno del dependiente se pasa al dependiente. Si PHPUnit reconstruye todo desde cero, ¿por qué proporciona esta sintaxis engañosa en primer lugar? –

+0

@edorian - Las expectativas son validadas, pero AFAIK no son despojadas. Esto sucede después de que el método de prueba se complete con éxito, pero antes de llamar a 'tearDown()'. Si bien podrías agregar más expectativas y llamadas al simulacro en 'tearDown()', eso no sería muy útil. –

2

No soy un chico de php, por lo que me corrija si estoy equivocado, pero las todas las pruebas unitarias están diseñados para funcionar en la siguiente secuencia,

Configuración -> función de prueba -> destruir.

así que se llama a la función de configuración y destrucción cada vez antes de ejecutar cualquier función de prueba. Esto se hace a propósito para preservar el propósito de las pruebas unitarias.

Si desea tener casos de prueba de unidades dependientes, entonces tiene que codificarlos, de esa manera en lugar de depender de las variables globales para hacerlo (¡esto anula el propósito de las pruebas unitarias!). Si hay un caso de prueba 'A' que depende de alguna función, llame a esa función desde 'A' y luego afirme los valores.

+0

El método destroy se llama 'tearDown()'. –

+0

¡eso es correcto! Lo siento, estaba hablando en términos generales. – Shaunak

Cuestiones relacionadas