Testes automatizados e TDD - Parte 2

Na segunda parte da nossa série sobre testes automatizados e TDD traremos mais alguns conceitos e exemplos práticos de testes automatizados utilizando PHP.

Testes automatizados e TDD - Parte 2
Photo by Jeswin Thomas on Unsplash

Na segunda parte da nossa série sobre testes automatizados e TDD mostraremos alguns exemplos práticos de testes automatizados utilizando PHP. Escolhi PHP por ser a linguagem que tenho maior domínio, mas ressalto que os conceitos aqui apresentados podem ser aplicados a qualquer linguagem de programação e ferramenta (com algumas adaptações, é claro).

Planejar testes

Antes de partirmos para o código, vale lembrar que em uma situação real nem sempre conseguiremos automatizar todos os testes, nesses casos, é necessário, fazer uma análise criteriosa de quais casos de teste devem ser automatizados. Alguns critérios de escolha são:

  • Funcionalidades importantes do sistema;
  • Funcionalidades utilizadas com frequência;
  • Casos de teste que envolvem riscos para o Negócio.

Nessa etapa são planejados o ambiente de teste utilizado, cronograma de execução, entregáveis gerados com a execução, equipe envolvida, escopo de automação, entre outros.

Tá na hora do código

Um pouco de contexto: imagine uma aplicação bem simples que consiste em um catálogo de programas (filmes/séries etc.) onde o usuário deve ser capaz de marcar como assistido/não assistido.

O programa (filme/série) tem as seguintes propriedades: id, nome, resumo e assistido. Nossa classe Movie.php ficou assim:

OBS: já estou utilizando alguns recursos novos do PHP 8, como propriedades nomeadas, mas nada impediria de extrair as propriedades de um array ou em parâmetros separados no construtor.

<?php

declare(strict_types=1);

class Movie 
{
  private $watched = false;

  public function __construct(
    private string $id,
    private string $name,
    private string $summary
  ) {}

  public function toggleWatched() {
    $this->watched = !$this->watched;
  }

  public function isWatched() {
    return $this->watched;
  }
}

Vamos ao teste

Nosso primeiro teste será bem simples: o usuário deve poder marcar um filme como assistido/não assistido. Faremos esse teste de duas maneiras (com e sem o auxílio de uma ferramenta - no caso o PHPUnit), ou seja, queremos se o método toggleWatched funciona conforme o esperado.

Nosso objetivo com esse primeiro teste é demonstrar o conceito conhecido como AAA (Arrange, Act, Assert) que consiste em uma divisão lógica do código em 3 etapas:

  1. Arrange: o setup inicial do teste, é aqui que definimos tudo que o teste utilizará;
  2. Act: é nessa etapa que executamos de fato o que queremos testar;
  3. Assert: por fim verificamos se o resultado da execução é o resultado esperado.

Nosso arquivo test.php ficou da seguinte maneira:

<?php

declare(strict_types=1);

require 'Movie.php';

// Arrange
// Instanciamos o objeto $movie
$movie = new Movie(
  id: '1',
  name: 'Brooklyn 99',
  summary: ''
);

// Act
$movie->toggleWatched();

// Assert
if ($movie->isWatched() === true) {
  echo "PASSED".PHP_EOL;
} else {
  echo "FAIL".PHP_EOL;
}

Vejamos os 3 A's em ação:

  1. Arrange: nessa etapa instanciamos um novo objeto da classe Movie;
  2. Act: nessa etapa chamamos o método toggleWatched que, conforme definimos, inverte o valor da propriedade $watched, que inicia como false;
  3. Assert: por fim verificamos se o resultado é o esperado, em caso de sucesso imprime no terminal PASSED, em caso de falha mostra FAIL.

Para executar esse teste basta executar o comando php test.php.

Resultado do teste

Como vimos, é perfeitamente possível realizar testes automatizados sem o auxílio de nenhuma ferramenta, porém à medida que os testes crescem e ficam mais complexos sua programação também ficará mais complexa.

Além disso as ferramentas possuem uma interface mais amigável, facilitando a visualização de quais erros passaram e quais falharam.

Utilizando o PHPUnit

Primeiramente precisamos instalar o phpunit, utilizaremos o composer para isso com o comando sugerido pela documentação oficial composer require --dev phpunit/phpunit ^9.5.

Por convenção o PHPUnit procura algumas condições específicas para executar os testes, são elas:

  1. O arquivo da classe de teste deve terminar em Test - é recomendado utilizar [nomeDaClasseTestada]Test;
  2. Os métodos da classe de teste devem iniciar com test - exemplo : testMetodoIsTrueDeveRetornarTrue
  3. Os métodos de teste devem ter pelo menos um assert.
  4. A classe de teste deve extender de PHPUnit\Framework\TestCase.

NOTA: Lembrando que todas essas convenções podem ser configuradas no arquivo phpunit.xml, mas não entraremos nesse nível detalhe nesse momento.

A seguir vamos alterar nosso arquivo test.php seguindo as convenções do PHPUnit, sendo assim renomeamos o arquivo para MovieTest.php e seu conteúdo ficou assim:

<?php declare(strict_types=1);

require 'Movie.php';

use PHPUnit\Framework\TestCase;

class MovieTest extends TestCase
{
  public function testToggleWatched()
  {
    // Arrange
    // Instanciamos o objeto $movie
    $movie = new Movie(
      id: '1',
      name: 'Brooklyn 99',
      summary: ''
    );

    // Act
    $movie->toggleWatched();

    // Assert
    $this->assertTrue($movie->isWatched());
  }
}

Perceba que mantivemos a estratégia dos 3 A's apenas ajustamos os métodos para que o PHPUnit encontrasse os testes para execução. Quando executamos o comando php vendor/bin/phpunit MovieTest.php temos o seguinte resultado:

Resultado do PHPUnit

O resultado do teste nos mostra que 1 teste foi executado e 1 assertion passou. Se utilizarmos o parâmetro --colors temos um resultado mais amigável:

Resultado do PHPUnit com --colors

Em caso de falha o PHPUnit também nos mostra qual classe / assertion a falha ocorreu:

Resultado da falha PHPUnit

Dessa forma fica muito mais fácil e rápido identificar qual teste falhou.

Conclusão

Nessa parte vimos mais alguns conceitos sobre testes automatizados, a importância de planejar os testes, como compor e escrever um teste, com e sem o auxílio de ferramentas. Também vimos as facilidades que a utilização de ferramentas especializadas em testes automatizados nos conferem, tanto na visualização, quanto na execução dos testes.

Apesar de utilizarmos PHP e o PHPUnit nos exemplos esses conceitos podem ser aplicados em qualquer framework ou linguagem.

Na próxima parte entraremos em conceitos um pouco mais avançados no mundos dos testes: dublês de teste. Falaremos sobre os tipos de dublês de testes e traremos exemplos de como utilizá-los no PHPUnit. Nos vemos lá!