Skip to content

Latest commit

 

History

History
478 lines (343 loc) · 22.6 KB

README_pt-BR.md

File metadata and controls

478 lines (343 loc) · 22.6 KB

Logo do Bypass

Bypass para PHP

Sobre | Instalação | Escrevendo Testes | Exemplos | Créditos | Inspiração

Bandeira dos USA em base64 Bandeira do BRA em base64

Testes GitHub tag (último por data) Downloads no Packagist Licença no Packagist Última Atualização


Sobre

O Bypass para PHP prove uma maneira rápida para você criar um Servidor HTTP personalizado que retorne resposta predefinidas as requisições do seu cliente.

Ele é muito útil em ambientes de testes, onde seu aplicativo realiza requisições HTTP a serviços externos e você precisa simular diferentes situações, como por exemplo retornar dados específicos ou erros inesperados do servidor.


Instalação

📌 O Bypass requer o uso do PHP 8.0 ou superior.

Para instalar o Bypass através do composer, execute o seguinte comando:

composer require --dev ciareis/bypass

Vídeo demonstrativo

Testando nota fácil.io com Laravel e Bypass: Meu próprio sandbox

Escrevendo Testes

Conteúdo

📝 Nota: Se você deseja visualizar os códigos completos, vá para a seção Exemplos.

1. Abrindo o Servidor Bypass

Para escrever um teste, primeiro abra uma instância do servidor Bypass:

//Abre uma nova instância do Bypass Server
$bypass = Bypass::open();

O Bypass sempre será executado no URL http://localhost escutando através de uma porta de número aleatório.

Caso seja necessário, a porta pode ser especificada passando um valor para o argumento (int) $port:

//Abre uma nova instância do Bypass Server na porta 8081
$bypass = Bypass::open(8081);

2. Endereço e Porta do Bypass

O endereço(URL) e porta do servidor pode ser recuperados usando o método getBaseUrl():

$bypassUrl = $bypass->getBaseUrl(); //http://localhost:16819

Se você precisar recuperar apenas o número da porta, utilize o método getPort():

$bypassPort = $bypass->getPort(); //16819

3. Rotas

O Bypass serve dois tipos de rotas: A Rota Padrão, que pode retornar uma corpo de texto em formato JSON e a Rota de Arquivo, que pode retornar um arquivo binário.

Ao executar seus testes, você informará as rotas do Bypass para o seu aplicativo ou serviço, fazendo com que ele acesse os URLs do Bypass ao invés dos URLs do mundo real.

3.1 Rota Padrão

use Ciareis\Bypass\Bypass;

//Corpo de texto em formato JSON
$body = '{"username": "john", "name": "John Smith", "total": 1250}';

//Define uma rota que deverá retornar um corpo de texto (JSON) e o código HTTP 200
$bypass->addRoute(method: 'get', uri: '/v1/demo', status: 200, body: $body);

//Instânciando a classe DemoService
$service = new DemoService();

//Consumindo o serviço utilizando o URL fornecido pelo Bypass
$response = $service->setBaseUrl($bypass->getBaseUrl())
    ->getTotal();

//Suas asserções/verificações de teste ficam aqui...

O método addRoute() aceita os seguintes parâmetros:

Parâmetro Tipo Descrição
HTTP Method int $method Método de Requisição HTTP (GET/POST/PUT/PATCH/DELETE)
URI string $uri URI a ser servido pelo Bypass
Status int $status Código de Status HTTP a ser retornado pelo Bypass (padrão: 200)
Body string|array $body Corpo de texto (JSON) a ser servido pelo Bypass (opcional)
Times int $times Quantidade de vezes em que a rota deve ser acessada (padrão: 1)

3.2 Rota de Arquivo

use Ciareis\Bypass\Bypass;

//Lendo um arquivo PDF
$demoFile = \file_get_contents('storage/pdfs/demo.pdf');

//Define uma rota que deverá retornar o arquivo PDF e o código HTTP 200
$bypass->addFileRoute(method: 'get', uri: '/v1/myfile', status: 200, file: $demoFile);

//Instânciando a classe DemoService
$service = new DemoService();

//Consumindo o serviço utilizando o URL fornecido pelo Bypass
$response = $service->setBaseUrl($bypass->getBaseUrl())
    ->getPdf();

//Suas asserções/verificações de teste ficam aqui...

O método addFileRoute() aceita os seguintes parâmetros:

Parâmetro Tipo Descrição
HTTP Method int $method Método de Requisição HTTP (GET/POST/PUT/PATCH/DELETE)
URI string $uri URI a ser servido pelo Bypass
Status int $status Código de Status HTTP a ser retornado pelo Bypass (padrão: 200)
File binary $file Arquivo binário a ser servidor pelo Bypass
Times int $times Quantidade de vezes em que a rota deve ser acessada (padrão: 1)

3.3 Método Serve() do Bypass

O Bypass vem com uma variedade de atalhos convenientes para as requisições HTTP mais utilizadas.

Esses atalhos são chamados de "Métodos Assistentes para Rotas", porém para simplificar vamos chama-los apenas de "Assistentes para Rotas". Eles são servidos em uma porta aleatória e de forma automatica ao utilizarmos: Bypass::serve().

Ao servirmos as rotas por meio dos assistentes, não necessitamos de chamar Bypass::open().

Exemplo:

use Ciareis\Bypass\Bypass;
use Ciareis\Bypass\Route;

//Criando as rotas na inicialização do servidor
$bypass = Bypass::serve(
    Route::ok(uri: '/v1/demo/john', body: ['username' => 'john', 'name' => 'John Smith', 'total' => 1250]), //método GET, código HTTP 200
    Route::notFound(uri: '/v1/demo/wally') //método GET, código HTTP 404
);

//Instânciando a classe DemoService
$service = new DemoService();
$service->setBaseUrl($bypass->getBaseUrl());

//Consumindo o serviço utilizando a rota "OK (200)"
$responseOk = $service->getTotalByUser('john'); //200 - OK com total => 1250

//Consumindo o serviço utilizando a rota "Not Found (404)"
$responseNotFound = $service->getTotalByUser('wally'); //404 - Recurso não encontrado

//Suas asserções/verificações de teste ficam aqui...

No exemplo acima, o Bypass está servindo duas rotas: Uma rota acessível pelo método HTTP GET retornando um JSON com o código HTTP 200, e uma segunda rota sendo acessível também pelo método GET só que retornando apemas um código HTTP 404.

Assistentes para Rotas

Método Assistente Método HTTP Codigo HTTP Retorno Uso comum
Route::ok() GET 200 opcional (string|array) Requisição foi bem sucedida
Route::created() POST 201 opcional (string|array) Resposta a uma requisição em que resultou uma criação
Route::badRequest() POST 400 opcional (string|array) Algo incorreto na requisição (ex: parâmetro errado)
Route::unauthorized() GET 401 opcional (string|array) Não autenticado
Route::forbidden() GET 403 opcional (string|array) Autenticado, porém tentando acessar um recurso restrito (sem permissão)
Route::notFound() GET 404 opcional (string|array) URL ou recurso não existem
Route::notAllowed() GET 405 opcional (string|array) Método não permitido
Route::validationFailed() POST 422 opcional (string|array) Os dados enviados não satisfazem as regras de validação
Route::tooMany() GET 429 opcional (string|array) Requisição rejeitada devido a limitações do servidor
Route::serverError() GET 500 opcional (string|array) Geralmente indica que algum erro aconteceu no lado do servidor

Você também pode personalizar os assistentes de rotas de acordo com as suas necessidades, passando os seguintes parâmetros:

Parâmetro Tipo Descrição
URI string $uri URI a ser servido pelo Bypass
Body string|array $body Corpo de texto (JSON) a ser servido pelo Bypass (opcional)
HTTP Method string $method Método de Requisição HTTP (GET/POST/PUT/PATCH/DELETE)
Times int $times Quantidade de vezes em que a rota deve ser acessada (padrão: 1)

No exemplo abaixo, você pode ver o assistente Route::badRequest usando o método GET ao invés do método POST.

use Ciareis\Bypass\Bypass;
use Ciareis\Bypass\Route;

Bypass::serve(
    Route::badRequest(uri: '/v1/users?filter=foo', body: ['error' => 'O parâmetro filter de valor foo não existe.'], method: 'GET')
);

📝 Nota: Rotas personalizadas podem ser criadas usando uma Rota Padrão no caso de você necessitar de algo mais específico, não coberto pelos assistentes de rotas.

4. Verificando se Rotas Foram Chamadas

Você pode necessitar de verificar se uma rota foi acessada uma ou mais vezes.

O método assertRoutes() retorna um RouteNotCalledException no caso de uma rota não ter sido acessada por tantas vezes quanto forão definidas no parâmetro $times.

Se você precisa garantir que uma rota não está sendo acessada pelo seu serviço, defina o parâmetro como zero $times = 0.

//Corpo de texto em formato JSON
$body = '{"username": "john", "name": "John Smith", "total": 1250}';

//Define uma rota que deve ser acessada duas vezes
$bypass->addRoute(method: 'get', uri: '/v1/demo', status: 200, body: $body, times: 2);

//Instânciando a classe DemoService
$service = new DemoService();

//Consumindo o serviço utilizando o URL fornecido pelo Bypass
$response = $service->setBaseUrl($bypass->getBaseUrl())
    ->getTotal();

$bypass->assertRoutes();

//Suas asserções/verificações de teste ficam aqui...

5. Interromper ou encerrar

O Bypass vai encerrar seu próprio servidor uma vez que seu teste termine de ser executado.

O servidor do Bypass pode ser interrompido ou encerrado a qualquer momento com os seguintes métodos:

Para interromper: $bypass->stop();

Para encerrar: $bypass->down();

Exemplos

Caso de Uso

Para ilustrar melhor o uso do Bypass, imagine que você necessita escrever testes para um serviço chamado de TotalScoreService. Este serviço calcula a pontução total de um usuário específico em um jogo através de seu nome de usuário. Para obter a pontuação, este serviço deve consumir uma API fictícia hospedada no endereço emtudo-games.com/v1/score/::USERNAME::. A API consumida retorna o código de status HTTP 200 e um JSON contendo em seu corpo a lista de jogos:

{
  "games": [
    {
      "id": 1,
      "points": 25
    },
    {
      "id": 2,
      "points": 10
    }
  ],
  "is_active": true
}
use Ciareis\Bypass\Bypass;

//Abre uma nova instância do Bypass Server
$bypass = Bypass::open();

//Recuperando o URL de acesso do Bypass Server
$bypassUrl = $bypass->getBaseUrl();

//Corpo de texto em formato JSON
$body = '{"games":[{"id":1,"name":"game 1","points":25},{"id":2,"name":"game 2","points":10}],"is_active":true}';

//Definição de rota a ser acessada pelo serviço
$bypass->addRoute(method: 'get', uri: '/v1/score/johndoe', status: 200, body: $body);
    
//Instânciando TotalScoreService
$service = new TotalScoreService();

//Consumindo o serviço utilizando a URL retornada pelo Bypass Server
$response = $service
    ->setBaseUrl($bypassUrl) //Define o URL a ser usado pelo serviço
    ->getTotalScoreByUsername('johndoe'); //retorna 35

//Verificando se a resposta é 35 com Pest PHP
expect($response)->toBe(35);

//Verificando se a resposta é 35 com PHPUnit
$this->assertSame($response, 35);

Exemplos Rápidos de Testes

Clique abaixo para visualizar trechos de códigos utilizando Pest PHP e PHPUnit.

Pest PHP
use Ciareis\Bypass\Bypass;

it('properly returns the total score by username', function () {

    //Abre uma nova instância do Bypass Server
    $bypass = Bypass::open();

    //Corpo de texto em formato JSON
    $body = '{"games":[{"id":1,"name":"game 1","points":25},{"id":2,"name":"game 2","points":10}],"is_active":true}';

    //Definição de rota a ser acessada pelo serviço
    $bypass->addRoute(method: 'get', uri: '/v1/score/johndoe', status: 200, body: $body);

    //Instânciando e consumindo o serviço utilizando a URL retornada pelo Bypass Server
    $service = new TotalScoreService();
    $response = $service
        ->setBaseUrl($bypass->getBaseUrl())
        ->getTotalScoreByUsername('johndoe');

    //Verifica se a resposta é 35
    expect($response)->toBe(35);
});

it('properly gets the logo', function () {

    //Abre uma nova instância do Bypass Server
    $bypass = Bypass::open();

    //Lendo arquivo a partir do caminho informado
    $filePath = 'docs/img/logo.png';
    $file = \file_get_contents($filePath);

    //Definição de rota a ser acessada pelo serviço
    $bypass->addFileRoute(method: 'get', uri: $filePath, status: 200, file: $file);

    //Instânciando e consumindo o serviço utilizando a URL retornada pelo Bypass Server
    $service = new LogoService();
    $response = $service->setBaseUrl($bypass->getBaseUrl())
        ->getLogo();

    //Verifica se o arquivo retornado pelo Bypass é igual ao arquivo local
    expect($response)->toEqual($file);
});
PHPUnit
use Ciareis\Bypass\Bypass;

class BypassTest extends TestCase
{
    public function test_total_score_by_username(): void
    {
        //Abre uma nova instância do Bypass Server
        $bypass = Bypass::open();
        
        //Corpo de texto em formato JSON
        $body = '{"games":[{"id":1,"name":"game 1","points":25},{"id":2,"name":"game 2","points":10}],"is_active":true}';

        //Definição de rota a ser acessada pelo serviço
        $bypass->addRoute(method: 'get', uri: '/v1/score/johndoe', status: 200, body: $body);

        //Instânciando e consumindo o serviço utilizando a URL retornada pelo Bypass Server
        $service = new TotalScoreService();
        $response = $service
            ->setBaseUrl($bypass->getBaseUrl())
            ->getTotalScoreByUsername('johndoe');
        
        //Verifica se a resposta é 35 
        $this->assertSame(35, $response);
    }

    public function test_gets_logo(): void
    {
        //Abre uma nova instância do Bypass Server
        $bypass = Bypass::open();

        //Lendo arquivo a partir do caminho informado
        $filePath = 'docs/img/logo.png';
        $file = \file_get_contents($filePath);

        //Definição de rota a ser acessada pelo serviço
        $bypass->addFileRoute(method: 'get', uri: $filePath, status: 200, file: $file);

        //Instânciando e consumindo o serviço utilizando a URL retornada pelo Bypass Server
        $service = new LogoService();
        $response = $service->setBaseUrl($bypass->getBaseUrl())
            ->getLogo();

        //Verifica se o arquivo retornado pelo Bypass é igual ao arquivo local
        $this->assertSame($response, $file);
    }
}

Exemplos de Testes

📚 Veja exemplos completos de utilização do Bypass em testes com Pest PHP e PHPUnit para o exemplo de serviço GithubRepoService.

Créditos

E um agradecimento especial para o @DanSysAnalyst

Inspiração

Código inspirado no pacote Bypass