Yan Magalhães
Front End Developer at Ingresso Rápido
Uma introdução a testes unitários e TDD
1.
Study: Buggy software costs users, vendors nearly $60B annually
São testes que visam certificar o funcionamento de cada pequena parte do nosso sistema (classes e métodos), de forma isolada.
São os testes responsáveis por garantir o funcionamento da integração entre partes do sistema. Utilizamos classes do sistema que se comunicam, e a testamos de uma única forma, garantindo assim o funcionamento entre elas.
São os testes que garantem o funcionamento do sistema, verificando as funcionalidades implantadas e garantindo que o software realmente está fazendo o que fato era esperado.
Queremos adicionar produtos em um carrinho de compras, e depois descobrir qual é o produto de maior e menor valor.
class OrderProductTest extends PHPUnit
{
public function testSmallerAndLarguer()
{
$cart = new ShoppingCart();
$cart->add(
new Product("Celular", 900.00));
$cart->add(
new Product("Grill Churrasco", 550.00));
$cart->add(
new Product("Pen Drive", 100.00));
$order = new OrderProduct();
$order->find($carrinho);
$this->assertEquals("Pen Drive",
$order->getSmaller()->getName());
$this->assertEquals("Celular",
$order->getLarguer()->getName());
}
}
2.
Criado por Kent Beck, o Test Driven Development é uma metodologia de desenvolvimento de software, onde primeiro escrevemos o nosso teste e a partir daí, fazemos a implementação.
Iremos receber uma string que representa os números romanos, e em seguida, retornar o seu valor em decimal.
public function testReturnISymbol()
{
$rome = new RomeNumberConversor();
$number = $rome->convert("I");
$this->assertEquals(1, $number);
}
class RomeNumberConversor
{
public function convert($romeNumber)
{
return;
}
}
class RomeNumberConversor
{
public function convert($romeNumber)
{
return 1;
}
}
O tão famoso "passinhos de bebê" nos ajuda a entender melhor o contexto do problema. Sendo assim, buscamos fazer o teste passar, da forma mais simples e mais rápida possível.
/* Criando um Novo Teste */
public function testReturnVSymbol()
{
$rome = new RomeNumberConversor();
$number = $rome->convert("V");
$this->assertEquals(5, $number);
}
/* Alterando a implementação */
public function convert($romeNumber)
{
if ($romeNumber === "V") {
return 5;
}
return 1;
}
Processo pelo qual se realiza a reescrita do código que será utilizado para a solução final, com o objetivo de eliminar redundâncias, sem que ocorra a mudança do resultado final produzido.
/* Agora nosso código cobre os casos Fixos de conversao */
protected $fixedNumbers = array(
"I" => 1,
"V" => 5,
"X" => 10,
"L" => 50,
"C" => 100,
"D" => 500,
"M" => 1000
);
public function convert($romeNumber)
{
if (array_key_exists($romeNumber, $this->fixedNumbers)) {
return $this->fixedNumbers[$romeNumber];
}
return 0;
}
/* Novos testes para II, XXII */
public function testReturnsIISymbol()
{
$rome = new RomeNumberConversor();
$number = $rome->convert("II");
$this->assertEquals(2, $number);
}
public function testReturnsXXIISymbol()
{
$rome = new RomeNumberConversor();
$number = $rome->convert("XXII");
$this->assertEquals(22, $number);
}
/* Novos testes para IX, XXIV */
public function testReturnsIXSymbol()
{
$rome = new RomeNumberConversor();
$number = $rome->convert("IX");
$this->assertEquals(9, $number);
}
public function testReturnsXXIVSymbol()
{
$rome = new RomeNumberConversor();
$number = $rome->convert("XXIV");
$this->assertEquals(24, $number);
}
/* Implementacao final da conversao */
public function convert($romeNumber)
{
$sumNumbers = 0;
$rightLast = 0;
for ($i = strlen($romeNumber) - 1; $i >= 0; $i--) {
$now = 0;
$current = $romeNumber[$i];
if (array_key_exists($current,
$this->fixedNumbers)) {
$now = $this->fixedNumbers[$current];
}
$factor = 1;
if ($now < $rightLast) {
$factor = -1;
}
$sumNumbers += ($this->fixedNumbers[$current]
* $factor);
$rightLast = $now;
}
return $sumNumbers;
}
Permitem simular instâncias de uma classe, permitindo realizar ações nestas classes. Classes que por algum motivo ou outro, estão fora do nosso ambiente de testes, ou que não queremos utiliza-la
class Rainfall
{
public function __construct($institute)
{
$this->intitute = $institute;
}
public function average()
{
$total = 0;
for ($i=0;$i<5;$i++) {
$total += $this->institute->getLastRainfall();
}
return $total/5;
}
}
class RainfallTest extends PHPUnit_Framework_TestCase
{
public function tearDown()
{
Mockery::close();
}
public function testGetsAverageRainfall()
{
$institute = Mockery::mock('\Path\To\Institute');
$institute->shouldReceive('getLastRainfall')
->times(5)
->andReturn(70, 90, 150, 20, 50);
$raifallObj = new Rainfall($institute);
$this->assertEquals(76,
$raifallObj->getLastRainfall());
$this->assertFalse(100,
$raifallObj->getLastRainfall());
}
}
Artigo acadêmico de Nagappan, mostrando um estudo de caso da Microsoft e da IBM. Este estudo mostrou que em 4 produtos, o número de erros caiu entre 40 a 90%, em comparação com os mesmos produtos, que não utilizavam TDD.
3.
By Yan Magalhães
Palestra sobre Testes Unitários e TDD, apresentada na semana das Crianças na Donuz, em Betim, 06/10/2017