Testes unitários com PHPUnit
Autor: Pedro Ribeiro | Publicado: 2012-12-11T19:15:00-08:00


Olá pessoal.

Hoje irei demonstrar como implementar testes unitários com a utilização do PHPUnit.








Primeiro gostaria de complementar que, quem me incentivou a começar a escrever sobre este assunto, foi Paulo Victor, meu parceiro de blog e de grande amizade.... agora vamos codar!!


Dada as seguintes classes...











class&nbsp;Host


{





&nbsp; &nbsp;&nbsp;/** get hostname by cookie


&nbsp; &nbsp; &nbsp;*


&nbsp; &nbsp; &nbsp;* @return string


&nbsp; &nbsp; &nbsp;*/


&nbsp; &nbsp;&nbsp;public&nbsp;function&nbsp;getHost()


&nbsp; &nbsp;&nbsp;{


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// get hostname by cookie


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;$_COOKIE['host_cookie'];


&nbsp; &nbsp;&nbsp;}


}






----------------------------------------------------











class&nbsp;Options


{


&nbsp; &nbsp;&nbsp;/**


&nbsp; &nbsp; &nbsp;* @var Host


&nbsp; &nbsp; &nbsp;*/


&nbsp; &nbsp;&nbsp;private&nbsp;$host;





&nbsp; &nbsp;&nbsp;public&nbsp;function&nbsp;__construct(Host&nbsp;$host)


&nbsp; &nbsp;&nbsp;{


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$this-&gt;host&nbsp;=&nbsp;$host;


&nbsp; &nbsp;&nbsp;}





&nbsp; &nbsp;&nbsp;/**


&nbsp; &nbsp; &nbsp;* @return array


&nbsp; &nbsp; &nbsp;*/


&nbsp; &nbsp;&nbsp;public&nbsp;function&nbsp;getOptions()


&nbsp; &nbsp;&nbsp;{


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$options&nbsp;=&nbsp;array();


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;switch($this-&gt;host-&gt;getHost()){





&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;case&nbsp;'site.host2':


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$options['CACHE_ENABLED']&nbsp;=&nbsp;false;


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break;





&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;case&nbsp;'site.default':


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$options['CACHE_ENABLED']&nbsp;=&nbsp;true;


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break;


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;}


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;$options;


&nbsp; &nbsp;&nbsp;}


}






Acabei de desenhar o cenário que iremos enfrentar.

hora do entendimento.

Temos duas classes, a Host e a Options.

Para que vocês entendam o contexto, a classe Host é responsável por acessar uma informação que está no cookie, que no caso é o índice: 'host_cookie'.

Já a classe Options, utiliza da informação que receberá de uma instancia de Host, para retornar as opções, que é de sua responsabilidade.

Options recebe como injeção de dependência, um objeto Host no construtor.



OBS. :&nbsp;A intenção é &nbsp;realizar teste unitário na classe Options, iremos assumir que a classe Host já está coberta por testes.


Agora vai a primeira proposta para a classe de teste OptionsTest que obviamente irá testar a classe Options:







require_once&nbsp;ZF2_PATH.&nbsp;'/module/Tutorial/src/Tutorial/Component/Host/Options.php';









class&nbsp;OptionsTest&nbsp;extends&nbsp;PHPUnit_Framework_TestCase


{





&nbsp; &nbsp;&nbsp;public&nbsp;function&nbsp;testGetOptions()


&nbsp; &nbsp;&nbsp;{


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$expected&nbsp;=&nbsp;array('CACHE_ENABLED'&nbsp;=&gt;&nbsp;true);





&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$_COOKIE['host_cookie']&nbsp;=&nbsp;'site.default';


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$option&nbsp;=&nbsp;new&nbsp;Options(new&nbsp;Host());


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$result&nbsp;=&nbsp;$option-&gt;getOptions();





&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$this-&gt;assertEquals($expected,&nbsp;$result);


&nbsp; &nbsp;&nbsp;}





}






Entendendo o código...

Nesta classe de teste, apenas instanciamos a classe Options, passando uma nova instancia de Host, e realizamos a chamada do método getOpions().

Como sabemos que a classe Host irá buscar um valor na variavel global $_COOKIE, estamos definindo o valor necessário previamente.

OBS.: Quando testamos compotamento, já sabemos o que esperamos como retorno, então só temos que verificar se o retorno do método é o esperado, para isso utilizamos os métodos do PHPUnit, no código acima, foi utilizado o método assert, que verifica se ambos os argumentos são iguais.



Agora a refatoração.....


No exemplo mostrado para a classe de teste(OptionsTest), estamos dependendo do comportamento de outra classe, a Host, e isso não pode acontecer, pois estamos testando a classe Options e não podemos depender de fatores externos....Com isso iremos utilizar um Mock.

Um Mock, é um objeto que se passa por uma instancia de outra classe, e seus comportamentos serão pré definidos.

Iremos criar um mock para a classe Host, que é dependência para o construtor da classe Options, com isso nosso teste não dependerá da instancia real de um objeto Host, pois o código de teste irá utilizar o nosso Mock e nunca irá executar a classe Host.



OptionsTest (FINAL)







require_once&nbsp;ZF2_PATH.&nbsp;'/module/Tutorial/src/Tutorial/Component/Host/Options.php';


require_once&nbsp;ZF2_PATH.&nbsp;'/module/Tutorial/src/Tutorial/Component/Host.php';








class&nbsp;OptionsTest&nbsp;extends&nbsp;PHPUnit_Framework_TestCase


{





&nbsp; &nbsp;&nbsp;public&nbsp;function&nbsp;testGetOptions()


&nbsp; &nbsp;&nbsp;{


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$expected&nbsp;=&nbsp;array('CACHE_ENABLED'&nbsp;=&gt;&nbsp;true);





&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$mockedHost&nbsp;=&nbsp;$this-&gt;getMock('Host',&nbsp;array('getHost'),&nbsp;array(),&nbsp;'',&nbsp;false);





&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$mockedHost-&gt;expects($this-&gt;once())


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;-&gt;method('getHost')


&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;-&gt;will($this-&gt;returnValue('site.default'));





&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$option&nbsp;=&nbsp;new&nbsp;Options($mockedHost);


&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$result&nbsp;=&nbsp;$option-&gt;getOptions();





&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;$this-&gt;assertEquals($expected,&nbsp;$result);


&nbsp; &nbsp;&nbsp;}





}







OBS.: Ainda poderíamos explorar muito esse teste, pois teríamos que prever todas as situações para o método getOptions(), mas o post ficaria muito extenso.


Por enquanto é só pessoal, provavelmente irei escrever mais posts relacionados a PHPUnit, fiquem ligados.