Pregunta

Esta no es una pregunta tanto como un intento de salvar a alguien más la hora que solo desperdicié en Phpunit.

Mi problema era que mi objeto simulado, cuando se usaba en una prueba dependiente, no estaba devolviendo el valor esperado. Parece que Phpunit no preserva el mismo objeto entre las pruebas dependientes, aunque la sintaxis hace que parezca que lo hace.

¿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() );
}

}
¿Fue útil?

Solución

Los objetos simulados en phpunit se conectan a la instancia de prueba para la cual se crean, y esto por definición significa un solo método de prueba. La razón de esto es que PhPunit le permite especificar expectativas en un simulacro que debe satisfacerse durante una prueba. Para hacer esto, afirma esas expectativas una vez que el método termina con éxito. Si el simulacro viviera en las pruebas, las expectativas no funcionarían.

El problema es que esto no es compatible con los objetos Stub: simulacros que contienen solo acciones enlatadas que se tomarán en respuesta a los métodos y las entradas. Los trozos no validan que sus métodos se llamen como simulacros completos. Quizás phpunit podría beneficiarse de la capacidad de crear trozos en setUpBeforeClass() que no están vinculados a la instancia de prueba.

Su otra opción es usar una biblioteca de objetos simulados externos como Mofa o Fake.

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

Otros consejos

No soy un tipo PHP, así que corrígeme si me equivoco, pero todas las pruebas unitarias están diseñadas para ejecutarse en la siguiente secuencia,

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

Por lo tanto, la función de configuración y destrucción se llama 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 unitarios dependientes, debe codificarlos, de esa manera en lugar de depender de las variables globales para hacerlo (¡esto derrota 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 de 'A' y luego afirme los valores.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top