Introdução aos testes com PHPUnit - parte II
asserts e dataprovider

Na primeira parte dessa série mostrei como instalar o PHPUnit, configurar e iniciamos um projeto simples para vermos se tudo funcionou como o esperado. Mostrei algumas convenções que eu considero úteis para o desenvolvimento de testes e no teste mostrei um método que não expliquei o assertEquals.
O que é um assert?
A tradução seria afirmação, em testes de software ele é exatamente isso, são afirmações que queremos fazer, por exemplo, $this->assertTrue($variavel) queremos afirmar se a variável é verdadeira (True).
Então sempre que temos um assert queremos fazer uma afirmação e o PHPUnit nos traz uma série de asserts prontos para uso, você pode encontrar a lista completa aqui, mas na maior parte dos casos usamos:
- assertArrayHasKey()
- assertEquals()
- assertFalse()
- assertSame()
- assertTrue().
Pelo menos eu nunca usei muito mais que isso.
Para ficar mais claro vamos explicar no nosso exemplo anterior.
<?php
use App\Calculator;
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
/**
* @return void
* @test
*/
public function it_should_do_a_sum_and_return_the_result()
{
$sum = new Calculator();
$result = $sum->sum(10,8.5);
$this->assertEquals(18.5, $result);
}
}
Nós fazemos um assert $this->assertEquals(18.5, $result), isso seria o equivalente a fazermos um if (18.5 == $result).
Asserts
Como eu disse que temos alguns asserts como mais utilizados vou colocar aqui o que cada um deles faz embora o nome diga bastante sobre eles, se você usa alguma IDE com autocomplete ao digitar $this->assert* vai aparece uma lista com todos os asserts disponíveis.
- assertArrayHasKey - Verifica se existe uma chave em um array
- assertEquals - Verifica se o resultado são iguais
- assertFalse - verifica se a expressão é falsa
- assertTrue - verifica se a expressão é verdadeira
- assertSame - verifica se a expectativa e o resultado atual são iguais ===, ou seja, de mesmo valor e tipo, diferente de assertEquals que compara utilizando == no caso de assertEquals a string '1' é igual ao inteiro 1, isso não é verdade quando utilizamos assertSame.
Vamos criar mais cenários para nossa calculadora, afinal testamos apenas somando inteiro e float.
<?php
use App\Calculator;
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
/**
* @return void
* @test
*/
public function it_should_do_a_sum_and_return_the_result()
{
$sum = new Calculator();
$result = $sum->sum(10,8.5);
$this->assertEquals(18.5, $result);
}
/**
* @return void
* @test
*/
public function it_should_do_a_sum_negative_numbers()
{
$sum = new Calculator();
$result = $sum->sum(-1, -10);
$this->assertEquals(-11, $result);
}
/**
* @return void
* @test
*/
public function it_should_do_a_sum_two_float_numbers()
{
$sum = new Calculator();
$result = $sum->sum(10.5,8.5);
$this->assertEquals(19, $result);
}
/**
* @return void
* @test
*/
public function it_should_do_a_sum_two_integer_numbers()
{
$sum = new Calculator();
$result = $sum->sum(10,8);
$this->assertEquals(18, $result);
}
}
Criamos mais alguns cenários, testamos números negativos, apenas float, apenas inteiros e nossos testes passaram
Vemos que o código repete muitas vezes, estamos apenas variando o valor, isso vai contra algo que talvez já tenhamos ouvido falar DRY, ou seja, don't repeat yourself então para nos ajudar o phpunit tem algo que chamamos dataproviders.
DataProviders
Para utilizarmos dataproviders basta utilizarmos annotations. Annotations nada mais são do que sinalizadores especiais definidos em seus docblocks de método, um exemplo abaixo:
<?php
/**
* @annotationName Annotation value
*/
public function testFoo()
{
//
}
Existem muitas anotações no PHPUnit , mas a que queremos agora é a dataProvider.
PHPUnit define provedores de dados como:
Um método de teste pode aceitar argumentos arbitrários. Esses argumentos devem ser fornecidos por um método de provedor de dados.
Em termos gerais um dataProvider fornece uma massa de dados que pode simular vários cenários para seu teste, evitando assim a repetição de código.
Então ao invés de criar vários métodos criamos um método que recebe os parâmetros que queremos passar para o teste.
Utilizando o exemplo anterior vamos mexer na nossa classe de cálculo
<?php
use App\Calculator;
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
/**
* @dataProvider numbersDataProvider
* @test
*/
public function it_should_do_a_sum_and_return_the_result($number1, $number2, $expected)
{
$sum = new Calculator();
$result = $sum->sum($number1, $number2);
$this->assertEquals($expected, $result);
}
public function numbersDataProvider(): array
{
return [
[10, 20, 30],
[1.5, 10, 11.5],
[-10, 20, 10],
[1, -20, -19],
[1.8, 1.8, 3.6],
];
}
}
No método temos a anotação * @dataProvider numbersDataProvider, essa anotação define o nome do método que é o dataProvider e vai nos fornecer os dados.
O método numbersDataProvider vai retornar um array contendo outro array onde os 3 valores vão ser passados para o teste it_should_do_a_sum_and_return_the_result na mesma ordem em que foram chamados, eu costumo chamar o último parâmetro de expected que seria o que eu espero que o teste retorne.
Agora temos apenas um teste com 5 cenários diferentes. Ao rodar vamos ter o resultado abaixo.
PHPUnit 9.5.20 #StandWithUkraine
Runtime: PHP 8.1.4
Configuration: /var/www/html/phpunit.xml
..... 5 / 5 (100%)
Time: 00:00.034, Memory: 6.00 MB
OK (5 tests, 5 assertions)
Notem que no shell vamos ter o resultado OK (5 tests, 5 assertions) porque teste rodou 5 vezes e agora não temos mais duplicação de código.
Então hoje aprendemos sobre asserts, dataProviders e vimos como diminuir a duplicação de código nos nossos testes, utilizando apenas um teste para vários cenários. Com isso você já pode começar a criar alguns testes em cenários simples e até alguns mais complexos.
Na próxima parte vamos ver sobre dublês de testes, métodos estáticos e injeção de dependência.