Instalação
Artigos
Cursos
Loja
INSTALAÇÃO
ARTIGOS
CURSOS
EBOOKS
DOWNLOADS
LOJA
ARTIGOS
Testes unitários com a PHPUnit
Como aumentar a confiança no software, mesmo sob constantes mudanças
**O que é o PHPUnit?** O `PHPUnit` é um framework de testes unitários, criado por Sebastian Bergmann. Ele disponibiliza um ecossistema necessário para realizar testes de forma automatizada. **Por que usar o PHPUnit?** O `PHPUnit` é baseado na ideia que o desenvolvedor deve encontrar erros no código-fonte o mais rápido possível, se possível, antes mesmo do usuário reportar. Semelhante a outros frameworks de teste unitário, o `PHPUnit` usa *assertions* para verificar se o código-fonte está se comportando como o esperado. O objetivo do teste de unidade é isolar cada parte do programa e mostrar que as partes individuais estão corretas. Um teste unitário fornece uma condição rigorosa , que o código deve satisfazer. Como principal resultado, os testes de unidade podem encontram problemas no início do ciclo de desenvolvimento. Criando assim um processo de implementação mais organizado e com menos problemas.
# Instalando o PHPUnit O `PHPUnit` pode ser instalado de duas maneiras: - Arquivo PHP (PHAR) - Composer Para instalar o `PHPUnit` é necessário saber qual a sua versão do `PHP`. Para isso execute o seguinte comando no terminal do servidor: ```r $ php --version ``` **Versões suportadas** | Versão principal|Versão do `PHP`|Data de lançamento|Suporte termina em| |--|--|--|--| |[PHPUnit 8](https://phpunit.de/getting-started/phpunit-8.html)|PHP 7.2, PHP 7.3, PHP 7.4|01 de fevereiro, 2019 |05 de fevereiro, 2021 |[PHPUnit 7](https://phpunit.de/getting-started/phpunit-7.html)|PHP 7.1, PHP 7.2, PHP 7.3|01 de fevereiro, 2018| 07 de fevereiro, 2020 |[PHPUnit 6](https://phpunit.de/getting-started/phpunit-6.html)|PHP 7.0, PHP 7.1, PHP 7.2|03 de fevereiro, 2017| 01 de fevereiro, 2019 |[PHPUnit 5](https://phpunit.de/getting-started/phpunit-5.html)| PHP 5.6, PHP 7.0, PHP 7.1| 02 de outubro, 2015|02 de fevereiro, 2018 O `PHPUnit` tem um roteiro com detalhes sobre o que está planejado para [futuras versões](https://github.com/sebastianbergmann/phpunit/milestones). Dependendo da versão do seu `PHP` você vai precisar usar uma fonte diferente, para a sua versão selecione o link correto na tabela acima. Este tutorial usará o PHP 7.2. Dentro do diretório de um projeto `PHP` irei instalar o `PHPUnit` com o arquivo PHAR. ```r $ wget -O phpunit https://phar.phpunit.de/phpunit-7.phar $ chmod +x phpunit //Testar a instalação $ ./phpunit --version //O comando acima deve retorna a seguinte linha PHPUnit 7.3.5 by Sebastian Bergmann and contributors. ``` Você ainda pode instalar o `PHPUnit` com o composer: **Instalando com composer** Se você não tem o composer instalado: ```r $ sudo apt-get install composer ``` Instalando `PHPUnit`: ```r $ composer require --dev phpunit/phpunit ^7 //Testar a instalação $ ./vendor/bin/phpunit --version //O comando acima deve retorna a seguinte linha PHPUnit 7.3.5 by Sebastian Bergmann and contributors. ``` # Escrevendo o primeiro teste Ao escrever testes unitários, é importante seguir as convenções básicas definidas pelo `PHPUnit`: 1. Os testes para uma classe `Class` vão dentro de uma classe `ClassTest`. 2. `ClassTest` herda (na maioria das vezes) de `PHPUnit\Framework\TestCase`. 3. Os testes são métodos públicos que são nomeados `test*`. Ex: `testNomedoMedodo()`. 4. Dentro dos métodos de teste são usadas asserções tal como `assertEquals()`, que servem para validar que o método retorna um valor real equivale ao esperado. ## O que são asserções/assertions Asserções são métodos desenvolvidos pelo `PHPUnit` para assegurar que o valor de um teste é um valor esperado. São fornecidos 46 métodos de asserção, um para cada caso: - assertArrayHasKey() - assertClassHasAttribute() - assertArraySubset() - assertClassHasStaticAttribute() - assertContains() - assertContainsOnly() - assertContainsOnlyInstancesOf() - assertCount() - assertDirectoryExists() - assertDirectoryIsReadable() - assertDirectoryIsWritable() - assertEmpty() - assertEqualXMLStructure() - assertEquals() - assertFalse() - assertFileEquals() - assertFileExists() - assertFileIsReadable() - assertFileIsWritable() - assertGreaterThan() - assertGreaterThanOrEqual() - assertInfinite() - assertInstanceOf() - assertInternalType() - assertIsReadable() - assertIsWritable() - assertJsonFileEqualsJsonFile() - assertJsonStringEqualsJsonFile() - assertJsonStringEqualsJsonString() - assertLessThan() - assertLessThanOrEqual() - assertNan() - assertNull() - assertObjectHasAttribute() - assertRegExp() - assertStringMatchesFormat() - assertStringMatchesFormatFile() - assertSame() - assertStringEndsWith() - assertStringEqualsFile() - assertStringStartsWith() - assertThat() - assertTrue() - assertXmlFileEqualsXmlFile() - assertXmlStringEqualsXmlFile() - assertXmlStringEqualsXmlString() [Definição de todas as asserções](https://phpunit.readthedocs.io/pt_BR/latest/assertions.html#uso-estatico-vs-nao-estatico-de-metodos-de-assercao) ## Asserções mais comuns ### `assertTrue()` ```php assertTrue(bool $condition[, string $message = '']) ``` Reporta um erro identificado pela `$message` se a `$condition` é `false`. ### assertCount() ```php assertCount($expectedCount, $haystack[, string $message = '']) ``` Reporta um erro identificado pela `$message` se o número de elementos no `$haystack` não for `$expectedCount`. ### assertEmpty() ```php assertEmpty(mixed $actual[, string $message = '']) ``` Reporta um erro identificado pela `$message` se `$actual` não está vazio. ### assertEquals() ```php assertEquals(mixed $expected, mixed $actual[, string $message = '']) ``` Reporta um erro identificado pela `$message` se as variáveis `$expected` e `$actual` não são iguais. ### assertGreaterThan() ```php assertGreaterThan(mixed $expected, mixed $actual[, string $message = '']) ``` Reporta um erro identificado pela `$message` se o valor de `$actual` não é maior que o valor de `$expected`.
## Código-fonte do primeiro teste Você não precisa ter um projeto `PHP` para entender o funcionamento do `PHPUnit`, com um servidor web + PHP + PHPUnit já podemos obter resultados. Para ver o funcionamento do `PHPUnit` pela primeira vez, testaremos funções nativas do `PHP`. Crie um arquivo com o nome `VetorTest.php` com o seguinte código-fonte: ```php assertSame(0, count($vetor)); // Adiciona o valor foo no vetor array_push($vetor, 'foo'); // Testa se a posição 0 do vetor contém a string foo $this->assertSame('foo', $vetor[count($vetor)-1]); // Testa se as duas variáveis tem o mesmo tipo e valor // 1, quantas posições tem o vetor $this->assertSame(1, count($vetor)); // Testa se as duas variáveis tem o mesmo tipo e valor // foo, Retira o último valor do vetor $this->assertSame('foo', array_pop($vetor)); // Testa se as duas variáveis tem o mesmo tipo e valor // 0, quantas posições tem o vetor $this->assertSame(0, count($vetor)); } } ``` ## Executando o primeiro teste em linha de comando Dentro do diretório do arquivo `VetorTest.php` usaremos o seguinte comando: Instalação usando PHAR ```r $ ./phpunit VetorTest.php ``` Instalação via composer ```r $ phpunit VetorTest.php ``` O resultado esperado é: ``` PHPUnit 7.3.5 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 57 ms, Memory: 10.00MB OK (1 test, 5 assertions) ``` **Entendendo o retorno do teste** - Linha 1: Comentários do autor. - Linha 2: Status, número de testes, percentual de sucesso. - Linha 3: Tempo e memória usados. - Linha 4: Status final Para cada teste executado, a ferramenta de linha-de-comando do PHPUnit imprime um caractere para indicar o progresso: `.` → Impresso quando um teste é bem sucedido. `F` → Impresso quando uma asserção falha enquanto o método de teste está executando. `E` → Impresso quando um erro ocorre enquanto o método de teste está executando. `R` → Impresso quando o teste foi marcado como arriscado (veja [Testes arriscados](https://phpunit.readthedocs.io/pt_BR/latest/risky-tests.html#risky-tests)). `S` → Impresso quando o teste é pulado (veja [Testes Incompletos e Pulados](https://phpunit.readthedocs.io/pt_BR/latest/incomplete-and-skipped-tests.html#incomplete-and-skipped-tests)). `I` → Impresso quando o teste é marcado como incompleto ou ainda não implementado (veja [Testes Incompletos e Pulados](https://phpunit.readthedocs.io/pt_BR/latest/incomplete-and-skipped-tests.html#incomplete-and-skipped-tests)). # Testes usando banco de dados Mesmo sendo simples, o exemplo anterior é bem sucedido ao mostrar as capacidades do `PHPUnit`. Para dar um passo além iremos usar o `PHPUnit` para testar operações de um sistema utilizando banco de dados. Este exemplo é mais complexo, e usa um framework e *autoloader*. O objetivo deste exemplo, não é que você reproduza ele fielmente, mas adaptar para sua realidade. O código-fonte abaixo foi desenvolvido usando o Adianti Framework. [projeto](https://github.com/pablodalloglio/phpoo) **Classe de teste:** ```php nome = 'Teste'; $cliente->store(); // Cria uma venda para o cliente $venda = new Venda; $venda->cliente = $cliente; $venda->data_venda = date('Y-m-d'); $venda->desconto = 10; $venda->acrescimos = 5; // Vetor de produtos $produtos = [1,2,3]; $total_produtos = 0; // Adiciona os produtos na venda foreach ($produtos as $id_produto) { $venda->addItem($produto = new Produto($id_produto), 1); $total_produtos += $produto->preco_venda; } // Salva a venda $venda->valor_venda = $total_produtos; $venda->valor_final = $total_produtos + $venda->acrescimos - $venda->desconto; $venda->store(); // Gera um débido com as devidas parcelas $parcelas = 5; Conta::geraParcelas($cliente->id, 2, $venda->valor_final, $parcelas); // Verifica se o valor da venda é igual ao dos débidos do cliente // por se tratar de um novo cliente o único débito é a venda realizada em execução $this->assertEquals( $venda->valor_final, $cliente->totalDebitos()); // Fechamento da transação comentado para // evitar que as operações sejam comitadas para o banco de dados #Transaction::close(); } } ``` Ao utilizar outras classes dentro do teste é necessário carregar automaticamente as classes necessárias, isso é conhecido como *autoloader*, [acesse para mais informações](http://php.net/manual/pt_BR/language.oop5.autoload.php). Ao utilizar o `PHPUnit` por linha de comando você pode usar um arquivo `PHP` para gerenciar o carregamento automatico de classses: ```r $ ./phpunit --bootstrap autoloader.php ContaTest.php ``` Da maneira exemplificada acima, as classes que forem usadas no arquivo ContaTest, serão automaticamente carregadas para a execução. O resultado do teste foi: ```r PHPUnit 7.3.5 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 62 ms, Memory: 10.00MB OK (1 test, 1 assertion) ``` **Simulando um erro** A linha de assert do exemplo anterior foi modificada para: ```php $this->assertEquals( $venda->valor_final-1, $cliente->totalDebitos()); ``` O `-1` foi adicionado para demostrar o resultado de um teste unitário com erro: ```r PHPUnit 7.3.5 by Sebastian Bergmann and contributors. F 1 / 1 (100%) Time: 75 ms, Memory: 10.00MB There was 1 failure: 1) ContaTest::testNewAccounts Failed asserting that 250.0 matches expected 249.0. /var/www/html/livro/App/Tests/ContaTest.php:36 FAILURES! Tests: 1, Assertions: 1, Failures: 1. ```
COMENTE SOBRE