O phpunit é um framework para testes unitários em PHP. Esse framework inclui funcionalidades para utilização de mocks. No entanto, temos uma limitação pois não é possível mockar funções do PHP. Nesse post vou mostrar um possível workaround para esta situação.
Vamos supor o código abaixo:
public function get($params) {
// A partir dos $params passados, montamos uma $url
return file_get_contents($url);
}
O código acima recebe um array de parâmetros. A partir dele, monta a url de um webservice REST. Por fim, faz um HTTP GET nessa url e retorna o xml recebido. A responsabilidade deste método é montar a url corretamente a partir dos parâmetros recebidos. Este é o comportamento que deve ser testado. O GET é feito pela função file_get_contents.
Idealmente queremos mockar a função file_get_contents. Mocks são usados com diversas finalidades. Neste exemplo, as duas principais motivações são: isolar o código de forma a só testar uma pequena unidade (montagem da url corretamente); e não fazer chamadas a um webservice real (por exemplo para não sobrecarregar um ambiente de produção ou porque o webservice não existe no ambiente de desenvolvimento.).
Já que não é possível mockar funções do PHP, como podemos mockar file_get_contents? Uma possível solução é criarmos uma classe cuja única responsabilidade é chamar esta função:
class FileGetContents {
function file_get_contents($url) {
return file_get_contents($url);
}
}
O código que queremos testar fica como abaixo. Note que uma instância de FileGetContents é injetada via construtor:
function ClienteHttp($fileGetContents) {
$this->fileGetContents = $fileGetContents;
}
public function get($params) {
// A partir dos $params passados, montamos uma $url
return $this->fileGetContents->file_get_contents($url);
}
E podemos mockar a classe FileGetContents no nosso teste:
/**
* @test
*/
public function deveria_chamar_webservice_com_url_correta() {
$urlEsperada = "http://url.esperada";
$fileGetContents = $this->getMock('FileGetContentsMock',array('file_get_contents'));
$fileGetContents->expects($this->once())
->method('file_get_contents')->with($this->equalTo($urlEsperada))
$clienteHttp = new ClienteHttp($fileGetContents);
$parametros; // array de parâmetros que devem resultar na $urlEsperada
$clienteHttp->get($parametros);
}
Alguns podem argumentar – corretamente – que a classe FileGetContents não é testada. Mas se levarmos em conta que ela simplesmente delega para uma outra função, a desvantagem não é tão significativa assim. Vale enfatizar a importância de que essa classe não tenha nenhum outro comportamento. Caso contrário, a falta de testes já não seria mais aceitável.
Posted by Guilherme Cirne