unitarias tutorial pruebas example docs descargar composer php unit-testing mocking phpunit

pruebas - phpunit tutorial



PHPUnit funciĆ³n simulada? (5)

¡Esto debería funcionar!

use MyProject/baz; class YourTestCase { /** @var callable **/ protected $mockFoo; /** @var callable **/ protected $fakeFoo; public function setUp() { if (function_exists(''foo'')) { $this->mockFoo = function($foosParams) { foo($foosParams); // Extra Stuff, as needed to make the test function right. }; } $this->fakeFoo = function($foosParams) { // Completely mock out foo. }; } public function testBazWithRealFoo() { if (!$this->mockFoo) { $this->markTestIncomplete(''This system does not have the "/Foo" function.''); } $actualResults = baz($n, $this->mockFoo); $this->assertEquals(''...'', $actualResults); } public function testBazWithMyFoo() { $actualResults = baz($n, $this->fakeFoo); $this->assertEquals(''...'', $actualResults); } }

Luego modifique su código existente:

if (function_exists(''foo'') && ! function_exists(''baz'')) { /** * Baz function * * @param integer $n * @return integer */ function baz($n) { return foo() + $n; } } namespace MyProject { function baz($bazParams, callable $foo = ''/foo'') { return $foo() + $bazParams; } }

Luego, en lugar de llamar a baz($n) , necesita llamar:

use MyProject/baz; baz($bazParams);

Es como la inyección de dependencia para funciones, yo ;-)

Tengo un escenario interesante en el sentido de que necesito que se defina una función para hacer pruebas para otra función. La función que quiero probar es algo como esto:

if (function_exists(''foo'') && ! function_exists(''baz'')) { /** * Baz function * * @param integer $n * @return integer */ function baz($n) { return foo() + $n; } }

La razón por la que estoy comprobando la existencia de foo es porque puede o no definirse en el proyecto de un desarrollador y la función que baz confía en foo . Debido a esto, solo quiero que se defina baz si puede llamar a foo .

El único problema es que hasta ahora ha sido imposible escribir pruebas. Intenté crear una secuencia de comandos de arranque en la configuración de la unidad PHP que definiría una función falsa de foo y luego requeriría el autocargador Composer, pero mi secuencia de comandos principal aún cree que foo no está definida. foo no es un paquete de Composer y no puede ser requerido por mi proyecto. Obviamente, Mockery tampoco funcionará para esto. Mi pregunta es si alguien con más experiencia con PHPUnit ha encontrado este problema y ha encontrado una solución.

¡Gracias!


¿Es esto suficiente?

to-test.php:

<?php if (function_exists(''foo'') && ! function_exists(''baz'')) { /** * Baz function * * @param integer $n * @return integer */ function baz($n) { return foo() + $n; } }

BazTest.php:

<?php class BazTest extends PHPUnit_Framework_TestCase { public function setUp() { function foo() { // Appropriate mock goes here return 1; } include __DIR__ . ''/to-test.php''; } public function testBaz() { $this->assertEquals(2, baz(1)); $this->assertEquals(3, baz(2)); } }

Ejecutando los rendimientos de la prueba:

PHPUnit 5.4.8 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 54 ms, Memory: 8.00MB OK (1 test, 2 assertions)


Agregarlo en el archivo bootstrap no funciona, ¿por qué?

¿Es porque a veces la función baz es (A) creada correctamente por el sistema y a veces (B) necesitas burlarte de ella? O (C) ¿siempre tienes que burlarte?

  • Caso A: ¿Por qué el código crea una función vital esporádicamente sobre la marcha?
  • Caso B: una función solo se puede registrar una vez, y nunca se puede cancelar ni sobrescribir. Por lo tanto, o vas con el simulacro o no. No se permite la mezcla.
  • Caso C: Si siempre necesita simularlo y agregarlo al archivo de rutina de carga, se definirá. Con respecto a lo que ha intentado, su archivo de arranque para phpunit no está cargado correctamente o escribió mal el nombre de la función.

Estoy seguro de que has configurado correctamente tu arranque de phpunit, pero en buena medida, se parece a lo siguiente:

/tests/phpunit.xml :

<phpunit bootstrap="phpunit.bootstrap.php" </phpunit>

/tests/phpunit.bootstrap.php :

<?php require(__DIR__ . "/../bootstrap.php"); // Application startup logic; this is where the function "baz" gets defined, if it exists if (function_exists(''foo'') && ! function_exists(''baz'')) { /** * Baz function * * @param integer $n * @return integer */ function baz($n) { return foo() + $n; } }

No cree la función baz sobre la marcha en sus pruebas, por ejemplo, en una función de setUp .

Las suites de prueba en phpunit usan el mismo bootstrapper. Por lo tanto, si necesita probar casos donde la función baz está definida y otros casos donde no está definida (y necesita burlarse de ella), necesita dividir la carpeta de tests , por ejemplo, en dos carpetas diferentes, cada una con su phpunit.xml Archivos phpunit.xml y phpunit.bootstrap.php . Por ejemplo, /tests/with-baz y /tests/mock-baz . Desde estas dos carpetas, ejecute las pruebas por separado. Simplemente cree enlaces simbólicos al phpunit en cada subcarpeta (por ejemplo, desde /test/with-baz cree ln -s ../../vendor/bin/phpunit si el compositor está en la raíz) para asegurarse de que ejecuta la misma versión de phpunit en Ambos escenarios.

La solución definitiva es, por supuesto, averiguar dónde se está definiendo la función baz e incluir manualmente el archivo de script culpable, si es posible, para garantizar la aplicación de la lógica correcta.

Alternativa

Use la anotación @runInSeparateProcess de @runInSeparateProcess y defina la función según sea necesario.

<?php class SomeTest extends /PHPUnit_Framework_TestCase { /** * @runInSeparateProcess */ public function testOne() { if (false === function_exists(''baz'')) { function baz() { return 42; } } $this->assertSame(42, baz()); } public function testTwo() { $this->assertFalse(function_exists(''baz'')); } }


Comience con un ligero refactor del código para hacerlo más comprobable.

function conditionalDefine($baseFunctionName, $defineFunctionName) { if(function_exists($baseFunctionName) && ! function_exists($defineFunctionName)) { eval("function $defineFunctionName(/$n) { return $baseFunctionName() + /$n; }"); } }

Entonces solo llámalo así:

conditionalDefine(''foo'', ''bar'');

Su clase de prueba PHPUnit contendrá las siguientes pruebas:

public function testFunctionIsDefined() { $baseName = $this->mockBaseFunction(3); $expectedName = uniqid(''testDefinedFunc''); conditionalDefine($baseName, $expectedName); $this->assertTrue(function_exists($expectedName)); $this->assertEquals(5, $expectedName(2)); } public function testFunctionIsNotDefinedBecauseItExists() { $baseName = $this->mockBaseFunction(3); $expectedName = $this->mockBaseFunction($value = ''predefined''); conditionalDefine($base, $expectedName); // these are optional, you can''t override a func in PHP // so all that is necessary is a call to conditionalDefine and if it doesn''t // error, you''re in the clear $this->assertTrue(function_exists($expectedName)); $this->assertEquals($value, $expectedName()); } public function testFunctionIsNotDefinedBecauseBaseFunctionDoesNotExists() { $baseName = uniqid(''testBaseFunc''); $expectedName = uniqid(''testDefinedFunc''); conditionalDefine($base, $expectedName); $this->assertFalse(function_exists($expectedName)); } protected function mockBaseFunction($returnValue) { $name = uniqid(''testBaseFunc''); eval("function $name() { return ''$value''; }"); return $name; }

Eso es suficiente para lo que pides. Sin embargo, puede refactorizar aún más este código extrayendo la generación de funciones en un generador de códigos. Eso es lo que puede escribir pruebas unitarias contra el generador para asegurarse de que crea el tipo de función que espera.


Parece que esta es una situación en la que necesitas simular la visibilidad, que definitivamente está fuera del ámbito de PHPUnit que es una biblioteca de clases. Por lo tanto, aún puedes usar PHPUnit, pero es posible que tengas que salir de él para lograr tu objetivo.

La única forma en la que puedo pensar para crear una función simulada es justo dentro del caso de prueba, sobre el objeto que se extiende / PHPUnit_Framework_TestCase.

function foo() { return 1; } if (function_exists(''foo'') && ! function_exists(''baz'')) { /** * Baz function * * @param integer $n * @return integer */ function baz($n) { return foo() + $n; } } class TestBaz extends /PHPUnit_Framework_TestCase { public function test_it() { $this->assertSame(4,baz(3)); } }

Es posible que tengas que crear dos archivos, uno con foo y otro sin, o cuatro si quieres probar foo / not foo y baz / not baz. Esta es definitivamente una opción fuera de la caja, que normalmente no recomendaría, pero en su caso, podría ser su mejor opción.