🔬 Lidando com Testes de Unidade e Declarações de Tipo em PHP
O Cenário: 🐕🧪
Durante meus estudos com testes de unidade em PHP com o PhpUnit, deparei-me com um desafio intrigante que desejo compartilhar. O cenário envolve a criação da classe "Dog" e a implementação de testes para garantir que a função "call" funcione de maneira esperada. Essa jornada tem como objetivo aprimorar minhas habilidades e práticas de programação.
Comecei desenvolvendo uma função de teste simples, que deveria lançar uma exceção quando um tipo de parâmetro inválido fosse fornecido. Infelizmente, a função de teste não produziu os resultados esperados, levando-me a investigar o motivo desse comportamento inesperado.
A função de Teste:
public function testWhenCallsDogWithInvalidTypeMustHaveException(): void
{
$dog = new Dog('Bob');
$this->expectException(TypeError::class);
$dog->call(11111);
}
Minha função "call" foi inicialmente implementada da seguinte forma:
public function call(string $name): bool
{
return $name == $this->name ? true : false;
}
No entanto, observei que, apesar de ter definido o tipo do parâmetro como "string", o PHP tentava forçar a conversão do argumento para uma string em vez de lançar uma exceção quando um tipo inválido era fornecido. Isso acontece devido à coerção automática de tipos do PHP.
A Solução Proposta: 🛠️🧪
Decidi validar manualmente o tipo do parâmetro e lançar uma exceção quando necessário (apesar de pensar que a declaração de tipo no parâmetro já servisse para isso):
public function call(string $name): bool
{
if (!is_string($name)) {
throw new \TypeError('The call() function expects a string argument.');
}
return $name == $this->name ? true : false;
}
Então eu rodei o teste novamente, muito feliz e contente mas a função de teste não passou.
A Descoberta Surpreendente: 🤯❗
Acreditei que ao declarar no início do arquivo declare(strict_types=1);
, poderia garantir que o PHP seguisse as declarações de tipo estritas e lançasse um TypeError quando houvesse uma discrepância.
No entanto, mesmo com essa declaração, meu teste ainda não passou.
A Experiência de Depuração: 🔍🐞
Para entender o motivo, criei um ambiente de teste online que reproduzisse a situação:
<?php
declare(strict_types=1);
class Dog
{
public function __construct(private string $name) {}
public function call(string $name): bool
{
return $name == $this->name ? true : false;
}
}
try {
echo (new Dog('Max'))->call(12) == true ? "Dog came to me" : "Dog did not come to me";
} catch (TypeError $e) {
echo "It threw on TypeError. \n";
}
?>
No entanto, nesse ambiente isolado, a exceção estava sendo lançada como esperado.
**A Busca Pela Resposta: ** 🔍📝
Após mais pesquisas, descobri que a declaração declare(strict_types=1); afeta apenas as chamadas de funções feitas no escopo do arquivo. Como meu teste utiliza o método call da classe "Dog", que não está no escopo global do arquivo, a declaração de tipos estritos não afeta seu comportamento.
A Conclusão: 💡
A razão pela qual o teste não passa está na natureza da declaração declare(strict_types=1);. Para garantir a aplicação de declarações de tipo estritas, elas devem ser utilizadas na própria função que está sendo testada.
Minha jornada de aprendizado nesse desafio me lembrou da importância de compreender a abrangência das declarações de tipo estritas em PHP e como elas impactam as diferentes partes do código.