Impulsione seus testes de PHP com Codeception
Publicados: 2022-03-11Antes de passar para Codeception e PHP, devemos cobrir o básico e começar explicando por que precisamos testar em aplicativos em primeiro lugar. Talvez pudéssemos concluir um projeto sem perder tempo com testes, pelo menos desta vez?
Claro, você não precisa de testes para tudo; por exemplo, quando você deseja construir mais uma página inicial. Você provavelmente não precisa de testes quando seu projeto contém páginas estáticas vinculadas por um roteador.
No entanto, você definitivamente precisa de testes quando:
- Sua equipe usa BDD/TDD.
- Seu repositório Git contém mais de alguns commits.
- Você é um profissional adequado, trabalhando em um projeto sério.
Você pode se desculpar dizendo que já tem um departamento de testes dedicado, um grupo de pessoas que realizam testes e escrevem novos quando necessário. Mas, você pode imaginar quanto tempo a correção de bugs levará depois que você adicionar novas funcionalidades ao seu projeto?
O que os testes resolvem?
Primeiro, vamos decidir que tipo de problemas podem ser resolvidos por meio de testes. Você não pode se livrar de todos os seus erros com testes, mas pode descrever o comportamento esperado nos casos de teste. Os erros podem estar dentro de seus casos de teste. O código de espaguete continua sendo código de espaguete mesmo quando você usa casos de teste.
No entanto, você pode ter certeza de que seu código será alterado posteriormente (corrigindo erros ou adicionando novos recursos), para que seu código ainda esteja livre de erros descritos no teste. Além disso, mesmo testes bem escritos podem às vezes ser usados na documentação, porque lá você pode ver como os cenários típicos se desenrolam e verificar o comportamento esperado. Podemos dizer que o teste é um investimento pequeno, mas crucial no futuro.
Então, que tipo de testes podemos empregar?
- Testes unitários: Testes de baixo nível que verificam pequenas partes do seu código - métodos de sua classe isolados de outro código.
- Testes integrais: Os testes integrais verificam uma parte da sua aplicação, eles podem conter várias classes ou métodos, mas devem ser restritos a um recurso. Este teste também deve verificar como as diferentes classes estão interagindo.
- Teste funcional: testa solicitações específicas para seu aplicativo: resposta do navegador, alterações no banco de dados e assim por diante.
- Teste de aceitação: Na maioria dos casos, o teste de aceitação significa verificar se o aplicativo atende a todos os requisitos do cliente.
Para esclarecer, digamos que ilustramos o processo com algo tangível, como um edifício. Um edifício é composto por pequenos blocos que formam paredes. Cada tijolo deve atender aos requisitos especificados; ele deve suportar a carga necessária, ter um volume e forma específicos e assim por diante. São testes unitários. A ideia dos testes de integração é verificar com que força e precisão os tijolos aderem uns aos outros, como eles estão integrados em um determinado elemento do edifício. Os testes funcionais podem ser comparados aos testes em uma única parede do edifício, para verificar se o interior está ou não protegido das intempéries e se é ou não possível ver o sol através da janela. O teste de aceitação envolve testar todo o edifício como um produto completo: abra a porta, entre, feche a porta, acenda a luz, suba ao segundo andar e dê uma olhada no jardim do lado de fora do prédio.
Conheça a Codecepção
No entanto, essa divisão é condicional e às vezes é difícil resistir à tentação de misturar diferentes tipos de testes.
Muitos desenvolvedores usam testes de unidade e afirmam que isso é suficiente. Eu costumava ser um desses desenvolvedores; Achei o uso de sistemas diferentes para diferentes tipos de testes muito difícil e demorado. Há algum tempo, decidi encontrar algo mais útil que o PHPUnit; Eu queria ser melhor em testar meu código, mas não queria ler e aprender toneladas de documentação e procurar armadilhas. Foi assim que descobri a Codeception. No começo, eu estava cético, como muitas vezes somos quando se trata de algo novo (este projeto tem cinco anos, então tecnicamente não pode ser considerado “novo”), mas depois de brincar com ele por alguns dias , concluí que o Codeception é um sistema muito útil e poderoso.
Então, como você instala o Codeception? É tão simples quanto parece:
$ composer require "codeception/codeception" $ php vendor/bin/codecept bootstrap
Após a instalação, você encontrará uma nova pasta chamada testes em seu projeto, e haverá algumas subpastas chamadas aceitação, funcional e unidade . Parece que podemos começar a escrever nossos testes. Legal, mas o que vem a seguir?
Agora, tente adicionar um teste Hello World de aceitação padrão.
$ php vendor/bin/codecept generate:cept acceptance HelloWorld
Agora, obtemos um arquivo de teste de aceitação tests/acceptance/HelloWorldCept.php, com o seguinte conteúdo:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('perform actions and see result');
A variável padrão, chamada $I
, não é apenas uma letra; é um personagem. O que conduz os testes? O testador, obviamente. Este testador abre a página ou classe do seu site, faz algo com ela e mostra o resultado final de suas ações. Você verá o que funcionou e o que deu errado. É por isso que esse objeto se chama $I
e contém métodos chamados wantTo()
, see()
ou amOnPage()
.
Então, vamos pensar como um testador pensaria sobre maneiras de verificar a operabilidade de uma página. A primeira abordagem é abrir a página e procurar uma frase. Isso prova que a página está disponível para os visitantes.
Isso deve ser fácil:
<?php $I->amOnPage('/'); $I->see('Welcome');
Podemos usar este comando para executar os testes do Codeception:
$ php vendor/bin/codecept run
Vemos imediatamente que algo está errado. À primeira vista, parece que a mensagem é muito longa e pouco clara, mas quando olhamos mais de perto, tudo se torna óbvio.
Fizemos um teste, Acceptance , e ele detectou um erro:
Acceptance Tests (1) Perform actions and see result (HelloWorldCept) Error ---------- 1) Failed to perform actions and see result in HelloWorldCept (tests/acceptance/HelloWorldCept .php) [GuzzleHttp\Exception\ConnectException] cURL error 6: Could not resolve host: localhost (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
Este é o culpado: localhost não está disponível.
E aqui estão as etapas do cenário do nosso teste:
1. $I->amOnPage("/")
Ok, vamos abrir tests/acceptance.suite.yml e alterar url: http://localhost/
para algo que esteja realmente disponível. No meu caso, é meu host de teste local, url: https://local.codeception-article.com/
Execute o teste novamente e é isso que você deve obter:
Acceptance Tests (1) --------------------------------------------------------------------------------------- Perform actions and result (HelloWorldCept) Ok
Viva! Nosso primeiro teste bem sucedido!
Claro, amOnPage()
não é o único método de teste disponível, nós apenas o escolhemos para nosso exemplo. Todos os métodos de teste de Codeception podem ser divididos nos seguintes grupos:
- Interação com a página:
fillField()
, selectOption(selectOption()
,submitForm()
,click()
- Asserções.
see()
,dontSee()
,seeElement()
,seeInCurrentUrl()
,seeCheckboxIsChecked()
,seeInField()
,seeLink()
. Para todos esses métodos, você pode adicionar um sufixo e usá-lo quando precisar de um método que não interrompa o cenário de teste quando algo não puder ser encontrado. - Métodos de cookies:
setCookie()
,grabCookie()
,seeCookie()
- Comentário e descrição dos cenários de teste:
amGoingTo()
,wantTo()
,expect()
. Use esses métodos para obter testes bem comentados e descritos, o que o ajudará a lembrar os objetivos do teste.
Então, se fôssemos testar a página de e-mail de redefinição de senha, poderíamos fazer assim:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('Test forgotten password functionality'); $I->amOnPage('/forgotten') $I->see('Enter email'); $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->expect('Reset password link not sent for incorrect email'); $I->see('Email is incorrect, try again'); $I->amGoingTo('Fill correct email and get link'); $I->see('Enter email'); $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->expect('Reset password link sent for correct email'); $I->see('Please check your email for next instructions');
Parece que isso deve fazer isso, mas e se houver algumas partes carregadas do Ajax na página? Podemos testar uma página como essa? A resposta é que o Codeception usa o PhpBrowser, baseado no Symfony BrowserKit e no Guzzle, por padrão. É simples, rápido e você só precisa enrolar para usá-lo.

Você também pode usar o Selenium e testar páginas com navegadores reais. Sim, será mais lento, mas você também poderá testar o JavaScript.
Primeiro, você precisa instalar o driver Selenium, alterar accept.suite.yml e reconstruir a classe AcceptanceTester. Depois disso, você pode usar os métodos wait()
e waitForElement()
. E, mais interessante, você poderá economizar seu tempo e recursos usando os métodos saveSessionSnapshot()
e loadSessionSnapshot()
. Este método permite armazenar o estado da sessão e iniciar novos testes em sessões anteriores. Isso é útil em algumas situações, por exemplo, no processo de autorização de teste.
Assim, acabamos com uma capacidade simples, mas poderosa, de testar muitas funções.
Teste funcional
OK, hora de passar para os testes funcionais.
$ php vendor/bin/codecept generate:cept functional HelloWorld
E é isso que obtemos:
<?php $I = new FunctionalTester($scenario); $I->amOnPage('/'); $I->see('Welcome');
Espere o que?
Não, não é um erro. Os testes funcionais devem ser escritos da mesma forma que os testes integrais. A diferença é que os testes funcionais estão interagindo diretamente com seu aplicativo. Isso significa que você não precisa de um servidor web para executar o teste funcional e tem mais capacidade para testar diferentes partes do seu aplicativo.
Isso significa que falta suporte para todos os frameworks, mas a lista de frameworks suportados é extensa: Symfony, Silex, Phalcon, Yii, Zend Framework, Lumen, Laravel. Isso deve ser suficiente para a maioria dos casos e para a maioria dos desenvolvedores. Por favor, consulte a documentação do módulo do Codeception para obter uma lista de funções disponíveis e, em seguida, basta ligá-lo functional.suite.yml
.
Antes de prosseguirmos com os testes unitários, permita-me fazer uma pequena digressão. Como você deve ter notado, criamos nossos testes com a chave cept:
$ php vendor/bin/codecept generate: cept acceptance HelloWorld
Esta não é a única maneira de criar testes. Existem também testes de cest . A diferença é que você pode estruturar vários cenários relacionados em uma classe:
$ php vendor/bin/codecept generate:cest acceptance HelloWorld <?php class HelloWorldCest { public function _before(AcceptanceTester $I) { $I->amOnPage('/forgotten') } public function _after(AcceptanceTester $I) { } // tests public function testEmailField(AcceptanceTester $I) { $I->see('Enter email'); } public function testIncorrectEmail(AcceptanceTester $I) { $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->see('Email is incorrect, try again'); } public function testCorrectEmail(AcceptanceTester $I) { $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->see('Please check your email for next instructions'); } }
Neste exemplo, os métodos _before()
e _after()
são executados antes e depois de cada teste. Uma instância da classe AcceptanceTester
é passada para cada teste, para que você possa usá-la da mesma forma que nos testes cest. Esse estilo de testes pode ser útil em determinadas situações, por isso vale a pena ter em mente.
Teste de unidade
Tempo para alguns testes de unidade.
Codeception é baseado em PHPUnit, então você pode usar testes escritos para PHPUnit. Para adicionar novos testes PHPUnit, use a seguinte abordagem:
$ php vendor/bin/codecept generate:phpunit unit HelloWorld
Ou apenas herde seus testes em \PHPUnit_Framework_TestCase
.
Mas se você quiser algo mais, tente os testes de unidade do Codeception:
$ php vendor/bin/codecept generate:test unit HelloWorld <?php class HelloWorldTest extends \Codeception\TestCase\Test { /** * @var \UnitTester */ protected $tester; protected function _before() { } protected function _after() { } // tests public function testUserSave() { $user = User::find(1); $user->setEmail('[email protected]'); $user->save(); $user = User::find(1); $this->assertEquals('[email protected]', $user->getEmail()); } }
Nada incomum por enquanto. Os métodos _before()
e _after()
são análogos de setUp()
e tearDown()
e serão executados antes e depois de cada teste.
A principal vantagem deste teste está em sua capacidade de estender seu processo de teste incluindo módulos que podem ser ativados em unit.suite.yml
:
- Acesso ao memcache e bancos de dados para rastrear alterações (MySQL, SQLite, PostgreSQL, MongoDB são suportados)
- Teste de aplicativos REST/SOAP
- Filas
Cada módulo tem suas próprias características, por isso é melhor verificar a documentação e coletar as informações necessárias para cada módulo antes de prosseguir com os testes reais.
Além disso, você pode usar o pacote Codeception/Specify (que precisa ser adicionado ao composer.json
) e escrever uma descrição assim:
<?php class HelloWorldTest extends \Codeception\TestCase\Test { use \Codeception\Specify; private $user; protected function _before() { $this->user = User::find(1); } public function testUserEmailSave() { $this->specify("email can be stored", function() { $this->user->setEmail('[email protected]'); $this->user->save(); $user = User::find(1); $this->assertEquals('[email protected]', $user->getEmail()); }); } }
O código PHP dentro dessas funções de fechamento é isolado, portanto, as alterações internas não afetarão o restante do seu código. As descrições ajudarão você a tornar o teste mais legível e facilitar a identificação de testes com falha.
Como um extra opcional, você pode usar o pacote Codeception\Verify
para sintaxe semelhante a BDD:
<?php public function testUserEmailSave() { verify($map->getEmail())->equals('[email protected]'); }
E é claro que você pode usar stubs:
<?php public function testUserEmailSave() { $user = Stub::make('User', ['getEmail' => '[email protected]']); $this->assertEquals('[email protected]', $user->getEmail()); }
Veredicto: a codecepção economiza tempo e esforço
Então, o que você deve esperar da Codeception? Para quem é? Existem ressalvas?
Na minha opinião, este framework de teste é adequado para todos os tipos de equipes diferentes: grandes e pequenos, iniciantes e profissionais de PHP experientes, aqueles que estão usando um framework popular e aqueles que não estão usando nenhum framework.
De qualquer forma, tudo se resume a isso: Codeception está pronto para o horário nobre.
É uma estrutura madura e bem documentada que pode ser facilmente estendida por vários módulos. Codeception é moderno, mas baseado no PHPUnit testado pelo tempo, que deve tranquilizar os desenvolvedores que não querem experimentar muito.
Ele tem um bom desempenho, o que significa que é rápido e não requer muito tempo e esforço. Melhor ainda, é relativamente fácil de dominar, e a documentação abundante deve ajudar em um processo de aprendizado sem complicações.
O Codeception também é fácil de instalar e configurar, mas possui muitas opções avançadas. Embora a maioria dos usuários não precise de todos (ou mesmo da maioria) deles, tudo depende do que você pretende fazer com eles. Você pode começar com o básico, e os recursos extras serão úteis mais cedo ou mais tarde.