PHPUnit – persistência e configurações avançadas

Neste artigo você verá como utilizar o PHPUnit para realizar testes com persistência de dados utilizando o Doctrine um ORM open-source e como definir configurações avançadas para personalizar sua suíte de testes e gerar relatórios de testes executados bem como cobertura do código de produção.  Começando Para começar o projeto crie um arquivo chamado composer.json.

Neste artigo você verá como utilizar o PHPUnit para realizar testes com persistência de dados utilizando o Doctrine um ORM open-source e como definir configurações avançadas para personalizar sua suíte de testes e gerar relatórios de testes executados bem como cobertura do código de produção.

 Começando

Para começar o projeto crie um arquivo chamado composer.json. Nele listaremos todos os pacotes/bibliotecas de terceiros que utilizaremos. Para este post utilizaremos o Doctrine e vários elementos do Zend Framework 2 além de é claro o próprio PHPUnit. Abaixo segue a lista de todas as bibliotecas que serão utilizadas.

Seguindo as recomendações da FIG, utilizaremos a PSR-0 que trata sobre a forma de carregarmento de classes na aplicação que estamos desenvolvendo. Com isso trabalharemos com namespaces e não precisaremos utilizar require ou include nas classes que utilizaremos. Para que o projeto tenha suas classes carregadas conforme a PSR-0 podemos informar isso no arquivo composer.json.

Isto nos diz que o namespace “Tableless” estará presente na pasta src e para isto se faz necessária a criação da pasta src e dentro da mesma a pasta Tableless. Há outra maneira de registrar o namespace através do _boostrap._php que seria algo como:

Feito isso baixamos o composer utilizando o comando curl -sS https://getcomposer.org/installer | php e em seguida instalamos as dependências através do comando php composer.phar install.

Com a instalação das dependências agora temos a nova estrutura contendo uma pasta vendor contendo todas as bibliotecas de terceiros, um novo arquivo composer.lock e composer.phar os quais já foram descritos em outro artigo sobre PHPUnit com composer e que pode ser acessado aqui.

Próximo passo

Agora temos de criar um arquivo que será o pontapé inicial da aplicação, arquivo este comumente nomeado de bootstrap conforme já mencionado anteriormente. Nele são configurados onde se encontram as entidades – que serão explicadas mais a frente deste tutorial, configuração do banco de dados entre outras configurações. Como neste exemplo será utilizado o Doctrine, precisamos configurar o mesmo.

Crie um arquivo chamado bootstrap.php na raiz de seu projeto, o fonte do bootstrap.php está comentado para melhor entendimento.

Até agora você viu várias vezes a palavra “Entidade” mas o que ela significa? Entidade é um objeto que tem um significado conceitual dentro de um domínio. Em outras palavras, cada entidade no Doctrine é a representação de uma tabela no banco de dados e cada registro é uma instância desta entidade. A entidade não manipula o banco de dados, apenas representa-o.

Pronto, a nível de produção já temos a configuração, agora criaremos a estrutura e configurações para testes.

Na raiz de seu projeto crie uma pasta chamada tests, dentro dela uma pasta chamada src e dentro da src uma pasta chamada Tableless. Perceba que o namespace ficará na mesma estrutura do código de produção, desta forma para utilizarmos uma entidade chamada User por exemplo, usaremos a seguinte declaração: use TablelessEntityUser;. Para a classe de testes de User se for necessária declarar em algum lugar será desta forma: use TablelessEntityUserTest;.

Após a criação das pastas necessárias falta a criação do bootstrap de testes e de um arquivo de configurações de execução do PHPUnit.

Começando com o bootstrap, o código novamente está comentado explicando porque determinadas coisas estão sendo feitas. Crie o arquivo bootstrap.php dentro da pasta de testes (tests).

O bootstrap de testes se faz necessário para sobrescrever a conexão com o banco de dados, caso contrário, todos os testes realizariam alterações no banco de dados de produção e isto jamais deve acontecer.

Feito isto agora é o momento de criar o arquivo xml de configurações do PHPUnit. Crie um arquivo chamado phpunit.xml dentro de sua pasta tests e adicione o conteúdo abaixo.

Quase pronto, se rodarmos o comando ./vendor/bin/phpunit -c tests/phpunit.xml dentro da raiz do projeto teremos a mensagem de que nenhum teste foi executado como na imagem abaixo.

 Como estamos trabalhando com um arquivo de configurações, para rodarmos o phpunit seguindo as definições do arquivo precisamos utilizar o parâmetro -c seguido do nome do arquivo.

Obviamente que nenhum teste ainda foi executado porque não temos nenhuma classe de testes. Vamos começar então. Crie uma pasta Entity dentro de tests/src. Dentro desta pasta crie um arquivo chamado UsertTest.php. A nova estrutura de testes deve estar como na imagem abaixo.

No arquivo UserTest.php adicione o namespace do mesmo que é TablelessEntity.

Agora definimos quais classes utilizaremos para este teste. Como estamos testando a entidade User precisaremos utilizar o TablelessEntityUser.

No entanto aí tem um detalhe. A entidade User ainda não existe, mas a criaremos dentro de instantes pois ainda temos uma classe que devemos criar antes mesmo da User. Ela se chama TestCase e deve estar no namespace TablelessTest. Crie em src/Tableless (não em tests/src/Tableless) uma pasta chamada Test e dentro dela um arquivo chamado TestCase.php.

Neste arquivo copie e cole o código abaixo que está comentado para melhor entendimento.

Pronto, já estamos com tudo o que precisamos para começar escrever os testes. Detalhe que esta configuração foi criada para que fosse possível utilizar e testar a persistência de dados utilizando o Doctrine. Para demais testes em controllers, services, views, forms ou o que mais você desejar esta configuração realizada até o momento permanece podendo ser acrescida de novos elementos, tudo depende da necessidade.

Criando o primeiro teste

No arquivo tests/src/Tableless/Entity/UserTest.php começaremos a definir nossos testes. Lembre-se que a ideia do TDD é que o teste seja criado antes do código de produção, e assim faremos.

Pra início de conversa utilizaremos a classe TestCase previamente criada e a entidade User.

A classe de testes atual (UserTest) extende de TestCase e adicionaremos o atributo protegido $entity.

Assim como a classe TestCase, nossa classe UserTest também possuirá um métdo setUp e um tearDown que servirão para as configurações da mesma. De momento apenas setaremos o valor default do atributo entity no setUp.

Agora segue o nosso primeiro teste: Novamente há comentários explicando cada ação.

Se rodarmos o comando ./vendor/bin/phpunit -c tests/phpunit.xml da raiz de nosso projeto deveremos ver o seguinte erro: “PHP Fatal error: Class ‘TablelessEntityUser …’” isto porque ainda não existe a entidade User pois realizamos o primeiro passo do TDD, o “Red”. Em seguida realizaremos o passo “Green” que consiste em criarmos o código que faça o teste passar e por último o passo “Refactor” que é onde faremos algumas melhorias no código. No código exemplo não existirá duplicidade e/ou partes inconsistentes com isso o Refactor realizará apenas algumas pequenas melhorias, nada mais.

Crie na pasta src (não em tests/src) uma pasta chamada Entity e dentro dela um arquivo chamado User.php. Eis a estrutura da entidade User. Por ser um arquivo muito extenso, colocarei apenas o link do mesmo que encontra-se no github. TablelessEntityUser

Perceba que existem comentários acima de cada um dos atributos da classe. Isto se dá por estarmos utilizando o Annotations do Doctrine para que os mesmos sejam lidos e mapeados no banco de dados. Em outras palavras, o Doctrine lê a anotação e cria a estrutura da tabela conforme as definições nos comentários. Há a possibilidade de realizar tais definições via xml e também via yaml o que não veremos neste tutorial.

Agora se rodarmos nosso teste novamente o mesmo passará. Ou seja, já temos um código minimamente testado com um início de noção de persistência de dados, veremos uma pequena melhora no código agora e em seguida algumas configurações para a execução dos testes.

./vendor/bin/phpunit -c tests/phpunit.xml

Definindo hash para senha

Primeiramente no teste adicionaremos uma asserção de que a senha do usuário registrado não é igual a senha que definimos, em string pura. Adicione o trecho de código abaixo em seu teste logo após $this->assertEquals($userData[‘name’], $registeredUser->getName());

Ao rodarmos o teste o mesmo deve quebrar pois ainda não criamos um hash para a senha, desta forma a senha fornecida está em string pura no banco de dados.

** ./vendor/bin/phpunit -c tests/phpunit.xml**

Agora, na classe TablelessEntityUser usaremos as seguintes classes do Zend:

em setPassword deixaremos adicionaremos a chamada ao método encryptPassword

E criaremos o método encryptPassword.

Agora rodando ./vendor/bin/phpunit -c tests/phpunit.xml o teste passa novamente.

Assim finalizamos o básico da realização de testes utilizando persitência de dados. A partir de agora veremos algumas configurações avançadas que lhe ajudarão muito no feedback dos testes.

Através do arquivo phpunit.xml podemos definir algumas configurações avançadas para a execução dos testes. Começando pela declaração . Atualmente encontra-se desta forma:

Isto quer dizer que utilizaremos um arquivo de bootstrap e dizemos qual arquivo é e também que queremos coloração no output. Caso colors=”true” não estivesse presente nossa visão ficaria desta forma.

Podemos definir erros e avisos sendo tratados como exceções.

E muitas outras opções. Para conhecer todas as opções de configurações acesse este link.

Certamente que a configuração a seguir é uma que empolga muitos desenvolvedores, logs e coverage. Com logs e coverage você identifica quais testes passaram, quais tiveram exceções, quais não passaram e o mais legal de tudo, o percentual de cobertura de testes que há em seu código de produção. Basicamente ao rodar um teste unitário, ele cobre uma pequena parte de seu código de produção, habilitando coverage você pode verificar quais linhas estão realmente garantidas por testes e quais você ainda tem de trabalhar mais tempo para garantir um mínimo de cobertura necessário para perfeito funcionamento mas principalmente para garantia de evolução de seu software.

Para criar logs utilizamos a tag logging no arquivo phpunit.xml logo após o fechamento da tag .

O log acima está gravando em formato de texto um checklist dos testes que existem em todas as classes de teste dentro da suite de testes marcando com x os que foram executados.

Rodando ./vendor/bin/phpunit -c tests/phpunit.xml será criada a pasta tests/data contento o arquivo testdox.txt O nome do arquivo é de sua escolha.

Também é possível gerar o testdox em formato html, basta alterar testdox-text para testdox-html e testdox.txt para testdox.html mas o mais comum é ser utilizado em formato txt mesmo.

Existe também a possibilidade de habilitar o testdox em tempo de execução. Basta apenas adicionar o parâmetro –testdox ao rodar os testes. O resultado será como abaixo.

Agrupamento de testes

Por certas vezes necessitamos agrupar testes para que rodemos somente determinada sequência sem que os demais sejam executados. Isto é útil para quando temos de realizar uma pequena alteração e não se faça necessário a execução de todos os testes já criados tornando o feedback mais rápido. Comumente isto é utilizado quando se deseja realizar um ajuste pontual e ao ser finalizado todos os testes são executados novamente.

O PHPUnit nos permite trabalhar com grupos os quais veremos sua definição a seguir.

Crie uma pasta chamada Filter em tests/src/Tableless e dentro dela um arquivo chamado CurrencyTest.php. O conteúdo deste arquivo está abaixo.

Perceba que antes de ser declarado o nome da classe existe uma anotação @group Filter. É isto que define o grupo ao qual este teste pertence. Faça o mesmo para o teste já existente (tests/src/Tableless/Entity/UserTest.php) anotando-o como @group Entity.

Agora que temos a definição dos grupos podemos rodar nossos testes somente de 1 grupo, de um conjunto de grupos ou de todos os grupos sem distinção. Existem duas formas de rodar os testes por grupos, através de parâmetro informado no momento da execução dos testes ou através do arquivo xml de configurações do PHPUnit, veremos ambas.

Via parâmetro

Somente um grupo

Mais de um grupo

Para que todos os grupos de testes sejam executados basta que não seja informado o parâmetro –group.

Via arquivo de configuração

No arquivo tests/phpunit.xml adicione uma tag e dentro dela liste os grupos desejados.

Você deve estar imaginando, se existe uma tag **include **deve existir uma tag **exclude **também. Imaginou certo! Dentro de include você adiciona todos os grupos que deseja que sejam executados nos testes já em exclude, todos que NÃO devem ser executados. O excclude é ideal para testes que foram marcados como incompletos ou pulados (skipped).

Após adicionar as tags referentes aos grupos de testes no arquivo xml de configurações não se faz mais necessário informar o parâmetro  –group, basta rodar normalmente.

Dentro de adicione uma nova tag chamada conforme o exemplo abaixo.

Basicamente estamos definindo que o coverage será em formato html, que o considerado baixo coverage será de 35% e um bom coverage se dará a partir de 70%. O highlight serve para destacar as linhas que foram cobertas com verde, não cobertas com vermelho e ignoradas permanecem com a cor padrão. Rode o teste novamente.

Agora no browser entre em seu localhost na pasta do projeto em que está trabalhando. Em seguida entre na pasta tests, após isto em data e por último em coverage. Surpreenda-se!

Navegando pelos arquivos você identificará o que já está bom e o que precisa ser mais testado. Neste nosso caso chegar a 100% é muito fácil, basta lermos todos os dados do usuário.

No arquivo tests/src/Tableless/Entity/UserTest.php, dentro do único teste que temos adicione os seguintes asserts:

Rode os testes novamente e corra pro abraço!

Lembrando novamente que para entendimento deste tutorial se faz necessário a leitura dos conteúdos anteriores sobre o tema, sendo eles TDD, por que usar?  e PHPUnit, como iniciar sem dores.

Bonus.

O que fizemos até o momento foi preparar o ambiente de testes e executá-los mas este ambiente ainda não está totalmente pronto para o código de produção pois precisamos de conexão com um banco de dados além de mais uma configuração do Doctrine para que possamos criar o banco de dados a partir de nossas entidades. Ou seja, lembra daquele processo de criar o banco de dados, definir as tabelas e relacionamentos todos antes do código? Com o Doctrine isto não se faz mais necessário, pode ser feito da forma descrita (e tradicional) mas há outra forma bem legal que é uma mão na roda e que mostrarei agora.

Primeiramente precisamos criar um arquivo de configuração para o cli (Command Line Interface) do Doctrine. Na pasta raiz de sua aplicação crie um arquivo chamado cli-config.php e cole o seguinte conteúdo:

Aparecerão várias opções de uso que vão desde checagem de status de conexão, validação das entidades, além de outras funcionalidades. Uma coisa que o doctrine não faz realmente é criar a base de dados pois isto depende de cada base pois Mysql é de um jeito, Postgres é de outro, SQL Server é de outro ainda, então esta tarefa ainda é manual.

Para fins didáticos criei uma base chamada tableless_tdd no mysql como definido no arquivo bootstrap.php da raiz do projeto. Você pode alterar o nome se quiser, bem como o próprio banco, experimente o Sqlite se quiser.

Com a base criada rode o comando ./vendor/bin/doctrine orm:validate-schema. Se estiver tudo ok aparecerá algo como a imagem abaixo. Nela informa que o mapeamento das entidades está correto mas o banco ainda não está sincronizado, para sincronizar rode o comando ./vendor/bin/doctrine orm:schema-tool:create. Isto lerá todas as entidades contidas em src/Tableless/Entity e criará a estrutura de tabelas a partir delas.

./vendor/bin/doctrine orm:validate-schema

./vendor/bin/doctrine orm:schema-tool:create

Atualmente possuímos somente a entidade User que indica que uma tabela users será criada no banco de dados. Após a finalização da execução do comando anterior seu banco de dados já estará com a nova estrutura. Ao realizar qualquer alteração na entidade User ou mesmo criar novas entidades você precisará rodar o comando ./vendor/bin/doctrine orm:schema-tool:update com isso aparecerá uma mensagem informando que já há uma estrutura no banco de dados e lhe pede confirmação sobre o que fazer. Você pode ignorar, ver as alterações ou forçar se tiver certeza do que está fazendo ou mesmo se já visualizou as alterações que serão realizadas e está ciente de que está tudo certo. Basta ler as intruções que o próprio Doctrine fornece que você saberá o que fazer, é muito intuitivo.

Finalizando

Agora que você já configurou o Doctrine, já conhece como criar testes unitários resta apenas aperfeiçoar a cada dia. Não existe uma receita, tudo requer empenho e dedicação mas que no final quando você ver aquelas barrinhas verdes mostrando 100% de cobertura se sentirá cada vez mais empolgado e com um código mais estável mas o principal, com um código que pode facilmente evoluir.

Para baixar o código-fonte gerado neste artigo acesse este link do Github.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *