Este paquete ayuda a ejecutar pruebas unitarias relacionadas con el tiempo/IO (por ejemplo, llamadas a funciones de suspensión, consultas de bases de datos, llamadas API, etc.) más rápido usando Swoole.
El paquete count permite ejecutar múltiples pruebas relacionadas con el tiempo/IO simultáneamente dentro de un único proceso PHP usando Swoole. Count es compatible con PHPUnit , lo que significa:
Un caso de prueba típico de countit se ve así:
use Deminy Counit TestCase ; // Here is the only change made for counit, comparing to test cases for PHPUnit.
class SleepTest extends TestCase
{
public function testSleep (): void
{
$ startTime = time ();
sleep ( 3 );
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
}
}
En comparación con PHPUnit , el conde podría hacer que sus casos de prueba sean más rápidos. Aquí hay una comparación cuando se ejecuta el mismo conjunto de pruebas usando PHPUnit y count para un proyecto real. En el conjunto de pruebas, muchas pruebas realizan llamadas al método DeminyCounitCounit::sleep() para esperar que suceda algo (por ejemplo, esperar a que caduquen los datos).
# de pruebas | # de afirmaciones | Hora de terminar | |
---|---|---|---|
count (sin Swoole), o PHPUnit | 44 | 1148 | 9 minutos y 18 segundos |
unidad (con Swoole habilitado) | 19 segundos |
El paquete se puede instalar usando Composer :
composer require deminy/counit --dev
O, en su archivo compositor.json , asegúrese de tener incluido el paquete deminy/counter :
{
"require-dev" : {
"deminy/counit" : " ~0.2.0 "
}
}
Las carpetas ./tests/unit/global y ./tests/unit/case-by-case contienen algunas pruebas de ejemplo, donde se incluyen las siguientes pruebas relacionadas con el tiempo:
Para ejecutar las pruebas de muestra, inicie los contenedores Docker e instale primero los paquetes de Composer:
docker-compose up -d
docker compose exec -ti swoole composer install -n
Se han iniciado cinco contenedores: un contenedor PHP, un contenedor Swoole, un contenedor Redis, un contenedor MySQL y un servidor web. El contenedor PHP no tiene la extensión Swoole instalada, mientras que el contenedor Swoole la tiene instalada y habilitada.
Como se dijo anteriormente, los casos de prueba se pueden escribir de la misma manera que los de PHPUnit . Sin embargo, para ejecutar pruebas relacionadas con el tiempo/IO más rápido con count , necesitamos hacer algunos ajustes al escribir esos casos de prueba; Estos ajustes se pueden realizar en dos estilos diferentes.
En este estilo, cada caso de prueba se ejecuta automáticamente en una rutina separada.
Para los casos de prueba escritos en este estilo, el único cambio que se debe realizar en los casos de prueba existentes es usar la clase DeminyCounitTestCase en lugar de PHPUnitFrameworkTestCase como clase base.
Un caso de prueba típico del estilo global se ve así:
use Deminy Counit TestCase ; // Here is the only change made for counit, comparing to test cases for PHPUnit.
class SleepTest extends TestCase
{
public function testSleep (): void
{
$ startTime = time ();
sleep ( 3 );
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
}
}
Cuando los métodos personalizados setUpBeforeClass() y tearDownAfterClass() se definen en los casos de prueba, asegúrese de llamar a sus métodos principales en consecuencia en estos métodos personalizados.
Este estilo supone que no hay afirmaciones inmediatas en los casos de prueba, ni afirmaciones antes de una llamada a la función sleep() o una operación IO compatible con rutinas. Los casos de prueba como los siguientes todavía funcionan, pero activarán algunos mensajes de advertencia cuando se prueben:
class GlobalTest extends Deminy Counit TestCase
{
public function testAssertionSuppression (): void
{
self :: assertTrue ( true , ' Trigger an immediate assertion. ' );
// ......
}
}
Podemos reescribir esta clase de prueba usando el estilo "caso por caso" (que se analiza en la siguiente sección) para eliminar los mensajes de advertencia.
Para encontrar más pruebas escritas en este estilo, consulte las pruebas en la carpeta ./tests/unit/global (conjunto de pruebas "global").
En este estilo, se realizan cambios directamente en un caso de prueba para que funcione de forma asincrónica.
Para casos de prueba escritos en este estilo, necesitamos usar la clase DeminyCounitCounit en consecuencia en los casos de prueba donde necesitamos esperar la ejecución de PHP o realizar operaciones IO. Normalmente, se utilizarán las siguientes llamadas a métodos:
Un caso de prueba típico del estilo caso por caso se ve así:
use Deminy Counit Counit ;
use PHPUnit Framework TestCase ;
class SleepTest extends TestCase
{
public function testSleep (): void
{
Counit:: create ( function () { // To create a new coroutine manually to run the test case.
$ startTime = time ();
Counit:: sleep ( 3 ); // Call this method instead of PHP function sleep().
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
});
}
}
En caso de que necesite suprimir el mensaje de advertencia "Esta prueba no realizó ninguna aserción" o hacer que coincida el número de aserciones, puede incluir un segundo parámetro al crear la nueva rutina:
use Deminy Counit Counit ;
use PHPUnit Framework TestCase ;
class SleepTest extends TestCase
{
public function testSleep (): void
{
Counit:: create ( // To create a new coroutine manually to run the test case.
function () {
$ startTime = time ();
Counit:: sleep ( 3 ); // Call this method instead of PHP function sleep().
$ endTime = time ();
self :: assertEqualsWithDelta ( 3 , ( $ endTime - $ startTime ), 1 , ' The sleep() function call takes about 3 seconds to finish. ' );
},
1 // Optional. To suppress warning message "This test did not perform any assertions", and to make the counters match.
);
}
}
Para encontrar más pruebas escritas en este estilo, consulte las pruebas en la carpeta ./tests/unit/case-by-case (conjunto de pruebas "case-by-case").
Aquí ejecutaremos las pruebas en diferentes entornos, con o sin Swoole.
#1
Ejecute los conjuntos de pruebas usando PHPUnit :
# To run test suite "global":
docker compose exec -ti php ./vendor/bin/phpunit --testsuite global
# or,
docker compose exec -ti swoole ./vendor/bin/phpunit --testsuite global
# To run test suite "case-by-case":
docker compose exec -ti php ./vendor/bin/phpunit --testsuite case-by-case
# or,
docker compose exec -ti swoole ./vendor/bin/phpunit --testsuite case-by-case
#2
Ejecute los conjuntos de pruebas usando count (sin Swoole):
# To run test suite "global":
docker compose exec -ti php ./counit --testsuite global
# To run test suite "case-by-case":
docker compose exec -ti php ./counit --testsuite case-by-case
#3
Ejecute los conjuntos de pruebas usando count (con la extensión Swoole habilitada):
# To run test suite "global":
docker compose exec -ti swoole ./counit --testsuite global
# To run test suite "case-by-case":
docker compose exec -ti swoole ./counit --testsuite case-by-case
Los dos primeros conjuntos de comandos tardan aproximadamente el mismo tiempo en finalizar. El último conjunto de comandos usa count y se ejecuta en el contenedor Swoole (donde la extensión Swoole está habilitada); por lo tanto es más rápido que los demás:
Estilo | # de pruebas | # de afirmaciones | Hora de terminar | |
---|---|---|---|---|
count (sin Swoole), o PHPUnit | global | 16 | 24 | 48 segundos |
caso por caso | 48 segundos | |||
unidad (con Swoole habilitado) | global | 7 segundos | ||
caso por caso | 7 segundos |
Dado que este paquete permite ejecutar múltiples pruebas simultáneamente, no debemos usar los mismos recursos en diferentes pruebas; de lo contrario, podrían producirse condiciones de carrera. Por ejemplo, si varias pruebas utilizan la misma clave de Redis, algunas de ellas podrían fallar ocasionalmente. En este caso, deberíamos utilizar diferentes claves de Redis en diferentes casos de prueba. Los métodos DeminyCounitHelper::getNewKey() y DeminyCounitHelper::getNewKeys() se pueden utilizar para generar claves de prueba aleatorias y únicas.
El paquete funciona mejor para pruebas que tienen la llamada a la función sleep() en uso; También puede ayudar a ejecutar algunas pruebas relacionadas con IO más rápido, aunque se aplican limitaciones. Aquí hay una lista de limitaciones de este paquete:
Hay imágenes prediseñadas deminy/counter para ejecutar las pruebas de muestra. Aquí están los comandos para construir las imágenes:
docker build -t deminy/counit:php-only -f ./dockerfiles/php/Dockerfile .
docker build -t deminy/counit:swoole-enabled -f ./dockerfiles/swoole/Dockerfile .
Este paquete permite utilizar Swoole para ejecutar múltiples pruebas relacionadas con el tiempo/IO sin multiprocesamiento, lo que significa que todas las pruebas se pueden ejecutar dentro de un único proceso PHP. Para entender cómo funciona exactamente, recomiendo consultar esta charla gratuita en línea: Programación CSP en PHP (y aquí están las diapositivas).
En el ecosistema PHP existen otras opciones para ejecutar pruebas unitarias en paralelo, la mayoría termina usando multiprocesamiento:
Licencia MIT.