Teste Unitário no PHPUnit para aplicar em arquivos do Magento 2

Lucas Teixeira dos Santos Santana - Oct 14 '22 - - Dev Community

PHPUnit

O PHPUnit é uma ferramenta de testes de unidade que disponibiliza um ecossistema necessário para realizar testes de forma automatizada em PHP. O PHPUnit está disponível para aplicações com o PHP a partir da versão 5.5.
Ele é instalado como uma dependência ao projeto e se adéqua com a estrutura do mesmo. Possui uma CLI (Interface de Linha de Comando) própria com opções de personalização para a forma de executar os testes.
O PHPUnit é baseado na ideia que o desenvolvedor deve encontrar erros no código-fonte o mais rápido possível. Semelhante a outros frameworks de teste unitário, ele usa assertions para verificar se o código-fonte está se comportando como o esperado.
Também suporta a declaração de dependências explícitas entre métodos de teste. Tais dependências não definem a ordem em que os métodos de teste devem ser executados, mas permitem o retorno de uma instância do ambiente do teste por um produtor e a passagem dele para os consumidores dependentes.

Para executar os testes com o PHPUnit, deve-se chamar o comando phpunit que estará dentro de ./vendor/bin/phpunit e referenciar o arquivo phpunit.xml com as configurações que serão utilizados para a execução dos testes.
O Magento 2 por padrão já possui um arquivo phpunit.xml que pode ser utilizado, ele se encontra no caminho ./dev/tests/unit/phpunit.xml.dist. O terceiro argumento passado no comando para a execução de testes será o caminho do módulo.

./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist app/code/{Vendor}/{Module}
./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist vendor/{vendor}/{module}
Enter fullscreen mode Exit fullscreen mode

phpunit.xml

Caso seja necessário personalizar a execução de testes (exemplo: ignorar a pasta API do módulo) é possível criar um arquivo phpunit.xml no próprio módulo e configurar para que os testes sejam executados de acordo com a configuração deste arquivo.

./vendor/bin/phpunit -c app/code/{Vendor}/{Module}/phpunit.xml.dist app/code/{Vendor}/{Module}
./vendor/bin/phpunit -c vendor/{vendor}/{module}/phpunit.xml.dist vendor/{vendor}/{module}
Enter fullscreen mode Exit fullscreen mode

Código base para a implantação

Para escrever testes com o PHPUnit no Magento 2 basta criar a estrutura de pastas \{Vendor}\{Module}\Test\Unit\{ClassDirectory}\{ClassName}Test estender a classe que \PHPUnit\Framework\TestCase. O PHPUnit localiza os métodos de teste pelo prefixo test no nome do método, e neste contém asserções para validar que o método retorne um valor real equivalente ao esperado.

Classe a ser testada:

<?php

namespace {Vendor}\{Module}\{ClassDirectory};

class {ClassName}
{
    public function {methodName}(): {type}
    {
        // Code here
    }
}
Enter fullscreen mode Exit fullscreen mode

Classe de teste:

<?php

namespace {Vendor}\{Module}\Test\Unit\{ClassDirectory};

use PHPUnit\Framework\TestCase;

class {ClassName}Test extends TestCase
{
    public function test{MethodName}(): {type}
    {
        // Code here
    }
}
Enter fullscreen mode Exit fullscreen mode

Asserções

Asserções são métodos desenvolvidos pelo PHPUnit para assegurar que o valor de um teste é um valor esperado, ou seja, sãos métodos que executam a unidade e certificam que o resultado gerado no teste é o esperado com sucesso ou falham.

Os métodos da classe \PHPUnit\Framework\TestCase que começam com o prefixo assert, são parte importante dos casos de teste e avaliam se certas condições, decisivas para determinar o sucesso ou falha do teste, geram um resultado esperado.

<?php

namespace {Vendor}\{Module}\Test\Unit\{ClassDirectory};

use PHPUnit\Framework\TestCase;

class {ClassName}Test extends TestCase
{
    public function test{MethodName}(): {type}
    {
        // Code here
        $this->assert{Name}({condition});
    }
}
Enter fullscreen mode Exit fullscreen mode

É possível verificar a documentação com todas as asserções fornecidas pelo PHPUnit no link “Asserções do PHPUnit“.

Exceções

Usando o método expectException(), o PHPUnit verifica se durante a exceção do teste é lançada uma exceção dentro do código testado. Além do método expectException() os métodos  expectExceptionCode(), expectExceptionMessage() e expectExceptionMessageRegExp() existem para configurar expectativas de exceções lançadas pelo código sob teste.

<?php

namespace {Vendor}\{Module}\Test\Unit\{ClassDirectory};

use PHPUnit\Framework\TestCase;

class {ClassName}Test extends TestCase
{
    public function test{MethodName}(): {type}
    {
        $this->expectException(\Exception::class);
        // Code here
    }
}
Enter fullscreen mode Exit fullscreen mode

Provedor de Dados

Com um método provedor de dados (data provider) o PHPUnit permite que o um método de teste seja executado em diferentes cenários, ou seja, consiste em preparar os dados de entradas para que o teste seja executado diversas vezes com dados diferentes.

Um método provedor de dados deve ser do tipo public, possuir a anotação @dataProvider como o nome do método e retornar um array de arrays. Cada item do array de retorno, o método de teste chamara como argumento.

<?php

namespace {Vendor}\{Module}\Test\Unit\{ClassDirectory};

use PHPUnit\Framework\TestCase;

class {ClassName}Test extends TestCase
{
    /**
     * @dataProvider {dataProviderName}
     */
    public function test{MethodName}({type} $data): {type}
    {
        // Code to test here
    }

    public function {dataProviderName}(): array
    {
        // Code to provide different input data

        return [
          [$data1],
          [$data2],
          [$data3],
          ...
          [$dataN]
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Métodos setUp e tearDown

O PHPUnit suporta compartilhamento do código de configuração. Os métodos-modelo setUp() e tearDown() são executados uma vez para cada método de teste (e em novas instâncias) da classe do caso de teste. Ambos os métodos devem ser do tipo protected e não possuir nenhum dado de retorno (void).

Antes que um método seja executado, um método modelo chamado setUp() é invocado. O método setUp() é onde os objetos que serão alvo dos testes são criados.

Uma vez que o método de teste tenha terminado sua execução, seja bem-sucedido ou falho, outro método modelo é invocado tearDown(). O método tearDown() é onde os objetos que foram alvo dos testes são limpos, normalmente, implementa-se o método tearDown() ao alocar recursos externos como arquivos ou sockets no setUp().

<?php

namespace {Vendor}\{Module}\Test\Unit\{ClassDirectory};

use PHPUnit\Framework\TestCase;

class {ClassName}Test extends TestCase
{
    protected function setUp(): void
    {
        // Code to set up test objects
    }

    protected function tearDown(): void
    {
        // Code to tear down test object
    }

    public function test{MethodName}(): {type}
    {
        // Code to test here
    }
}
Enter fullscreen mode Exit fullscreen mode

Dublê de Testes

É possível verificar a documentação com tudo sobre os dublês de testes do PHPUnit no link “Dublês de Testes do PHPUnit“.

Os métodos createMock($type) e getMockBuilder($type) fornecidos pelo PHPUnit podem ser usados em um teste para gerar automaticamente um objeto que possa atuar como um dublê de teste para a classe original especificada. Esse objeto de dublê de teste pode ser usado em cada contexto onde um objeto da classe original é esperado ou requerido.

$this->createMock({Class}::class):  este método retorna um objeto de dublê de teste para o tipo especificado (interface ou classe). A criação desse dublê de teste é realizada usando os padrões de boas práticas (os métodos __construct() e __clone() da classe original não são executados) e os argumentos passados para um método do dublê de teste não serão clonados.

$this->getMockBuilder({Class}::class): este método serve para customizar a geração do dublê de teste usando uma interface fluente. Por padrão, todos os métodos da classe original são substituídos com uma implementação simulada que apenas retorna null (sem chamar o método original). É possível configurar essas implementações simuladas para retornar um valor quando chamadas.

  • setMethods(array $methods): pode ser chamado no objeto getMockBuilder() para especificar os métodos que devem ser substituídos com um dublê de teste configurável. O comportamento dos outros métodos não muda. Se você chamar setMethods(null), então nenhum dos métodos serão substituídos (utilizado até a versão 7.3.X do PHP);
  • onlyMethods(array $methods): pode ser chamado no objeto getMockBuilder() para especificar os métodos que devem ser substituídos por um dublê de teste configurável. O comportamento dos outros métodos não muda. Cada método deve existir na classe que está sendo criada como dublê de teste (utilizado a partir da versão 7.4.X do PHP);
  • addMethods(array $methods): pode ser chamado no objeto getMockBuilder() para especificar os métodos que não existem (ainda) na classe de dublê de teste. O comportamento dos outros métodos permanece o mesmo (utilizado a partir da versão 7.4.X do PHP);
  • setConstructorArgs(array $args): pode ser chamado para fornecer um vetor de parâmetros que é passado ao construtor da classe original (que por padrão não é substituído com uma implementação falsa);
  • setMockClassName($name): pode ser usado para especificar um nome de classe para a classe de dublê de teste gerada;
  • disableOriginalConstructor(): pode ser usado para desabilitar a chamada ao construtor da classe original;
  • disableOriginalClone(): pode ser usado para desabilitar a chamada ao construtor clone da classe original;
  • disableAutoload(): pode ser usado para desabilitar o __autoload() durante a geração da classe de dublê de teste.

$this->method('{methodName}'): o método method('{methodName}') recebe o nome do método que será testado na classe é tera um retorno void.

$this->method('{methodName}')->willReturn('{valueToReturn}'): o método method('{methodName}') recebe o nome do método que será testado na classe. O método willReturn('{valueToReturn}') retorna um valor simples para corresponder ao teste, esta sintaxe é o mesmo que $this->method('{methodName}')->will($this->returnValue($value)).

$this->method('{methodName}')->returnSelf(): este método retorna o próprio objeto de dublê de teste.

$objectMock->expects($this->once())->method('{methodName}')->with($this->equalTo('something')): verifica se um método foi chamado, e com quais argumentos foi chamado, os métodos expects() e with() são para especificar como essa interação deve se parecer.

O método with() pode receber qualquer número de argumentos, correspondendo ao número de argumentos sendo falsos. É possível especificar restrições mais avançadas do que uma simples igualdade no argumento do método.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .