Este pacote ajuda a executar testes de unidade relacionados a tempo/IO (por exemplo, chamadas de função sleep, consultas de banco de dados, chamadas de API, etc.) mais rapidamente usando Swoole.
A contagem de pacotes permite executar vários testes relacionados a tempo/IO simultaneamente em um único processo PHP usando Swoole. Counit é compatível com PHPUnit , o que significa:
Um caso de teste típico de countit é assim:
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. ' );
}
}
Comparando com PHPUnit , countit poderia tornar seus casos de teste mais rápidos. Aqui está uma comparação ao executar o mesmo conjunto de testes usando PHPUnit e countit para um projeto real. No conjunto de testes, muitos testes fazem chamadas ao método DeminyCounitCounit::sleep() para esperar que algo aconteça (por exemplo, esperar que os dados expirem).
Nº de testes | Nº de afirmações | Hora de terminar | |
---|---|---|---|
count (sem Swoole) ou PHPUnit | 44 | 1148 | 9 minutos e 18 segundos |
contagem (com Swoole ativado) | 19 segundos |
O pacote pode ser instalado usando o Composer :
composer require deminy/counit --dev
Ou, em seu arquivo compositor.json , certifique-se de incluir o pacote deminy/counit :
{
"require-dev" : {
"deminy/counit" : " ~0.2.0 "
}
}
As pastas ./tests/unit/global e ./tests/unit/case-by-case contêm alguns testes de amostra, onde incluímos os seguintes testes relacionados ao tempo:
Para executar os testes de amostra, inicie os contêineres do Docker e instale os pacotes do Composer primeiro:
docker-compose up -d
docker compose exec -ti swoole composer install -n
Existem cinco contêineres iniciados: um contêiner PHP, um contêiner Swoole, um contêiner Redis, um contêiner MySQL e um servidor web. O contêiner PHP não possui a extensão Swoole instalada, enquanto o contêiner Swoole a possui instalada e habilitada.
Como dito anteriormente, os casos de teste podem ser escritos da mesma forma que os do PHPUnit . No entanto, para executar testes relacionados a tempo/IO mais rapidamente com countit , precisamos fazer alguns ajustes ao escrever esses casos de teste; esses ajustes podem ser feitos em dois estilos diferentes.
Nesse estilo, cada caso de teste é executado automaticamente em uma corrotina separada.
Para casos de teste escritos neste estilo, a única alteração a ser feita em seus casos de teste existentes é usar a classe DeminyCounitTestCase em vez de PHPUnitFrameworkTestCase como classe base.
Um caso de teste típico do estilo global é assim:
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. ' );
}
}
Quando os métodos personalizados setUpBeforeClass() e tearDownAfterClass() são definidos nos casos de teste, certifique-se de chamar seus métodos pais adequadamente nesses métodos personalizados.
Este estilo pressupõe que não há asserções imediatas em casos de teste, nem asserções antes de uma chamada de função sleep() ou de uma operação de E/S compatível com a rotina. Os casos de teste como os seguintes ainda funcionam, mas irão acionar algumas mensagens de aviso quando testados:
class GlobalTest extends Deminy Counit TestCase
{
public function testAssertionSuppression (): void
{
self :: assertTrue ( true , ' Trigger an immediate assertion. ' );
// ......
}
}
Podemos reescrever esta classe de teste usando o estilo “caso a caso” (discutido na próxima seção) para eliminar as mensagens de aviso.
Para encontrar mais testes escritos neste estilo, verifique os testes na pasta ./tests/unit/global (conjunto de testes "global").
Nesse estilo, você faz alterações diretamente em um caso de teste para fazê-lo funcionar de forma assíncrona.
Para casos de teste escritos neste estilo, precisamos usar a classe DeminyCounitCounit adequadamente nos casos de teste onde precisamos esperar pela execução do PHP ou para realizar operações IO. Normalmente, as seguintes chamadas de método serão usadas:
Um caso de teste típico do estilo caso a caso é assim:
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. ' );
});
}
}
Caso você precise suprimir a mensagem de aviso "Este teste não realizou nenhuma asserção" ou fazer com que o número de asserções corresponda, você pode incluir um segundo parâmetro ao criar a nova corrotina:
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 mais testes escritos neste estilo, verifique os testes na pasta ./tests/unit/case-by-case (conjunto de testes "caso a caso").
Aqui faremos os testes em diferentes ambientes, com ou sem Swoole.
#1
Execute os conjuntos de testes 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
Execute os conjuntos de testes usando count (sem 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
Execute os conjuntos de testes usando count (com a extensão 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
Os primeiros dois conjuntos de comandos levam aproximadamente o mesmo tempo para serem concluídos. O último conjunto de comandos usa contagem e é executado no contêiner Swoole (onde a extensão Swoole está habilitada); portanto, é mais rápido que os outros:
Estilo | Nº de testes | Nº de afirmações | Hora de terminar | |
---|---|---|---|---|
count (sem Swoole) ou PHPUnit | global | 16 | 24 | 48 segundos |
caso a caso | 48 segundos | |||
contagem (com Swoole ativado) | global | 7 segundos | ||
caso a caso | 7 segundos |
Como este pacote permite executar vários testes simultaneamente, não devemos utilizar os mesmos recursos em testes diferentes; caso contrário, poderão ocorrer condições de corrida. Por exemplo, se vários testes usarem a mesma chave Redis, alguns deles poderão falhar ocasionalmente. Nesse caso, devemos usar chaves Redis diferentes em casos de teste diferentes. Os métodos DeminyCounitHelper::getNewKey() e DeminyCounitHelper::getNewKeys() podem ser usados para gerar chaves de teste aleatórias e exclusivas.
O pacote funciona melhor para testes que possuem chamada de função sleep() em uso; Também pode ajudar a executar alguns testes relacionados a IO mais rapidamente, com aplicação de limitações. Aqui está uma lista de limitações deste pacote:
Existem imagens pré-construídas deminy/count para executar os testes de amostra. Aqui estão os comandos para construir as imagens:
docker build -t deminy/counit:php-only -f ./dockerfiles/php/Dockerfile .
docker build -t deminy/counit:swoole-enabled -f ./dockerfiles/swoole/Dockerfile .
Este pacote permite usar o Swoole para executar vários testes relacionados a tempo/IO sem multiprocessamento, o que significa que todos os testes podem ser executados em um único processo PHP. Para entender exatamente como funciona, recomendo conferir esta palestra online gratuita: Programação CSP em PHP (e aqui estão os slides).
No ecossistema PHP, existem outras opções para executar testes unitários em paralelo, a maioria acaba utilizando multiprocessamento:
Licença do MIT.