[Laravel Testing 101] Escrevendo testes para funcionalidades de usuários convidados em um aplicativo Laravel CRUD

Youghourta Benali Blocked Unblock Seguir Seguindo 2 de janeiro

Este é um trecho do meu próximo ebook Laravel Testing 101 . Se você ainda não leu os capítulos anteriores (disponível gratuitamente aqui: Adicionando testes ao seu aplicativo Laravel CRUD: Onde começar? E aqui: o que deveríamos estar testando em um aplicativo [laravel] CRUD? ), Faça isso antes lendo este aqui.

Agora que temos uma ideia melhor sobre as funcionalidades que deveríamos testar em nosso aplicativo Laravel, vamos começar testando o que um convidado pode fazer, já que é menos complicado do que um usuário logado pode fazer.

Como discutimos no capítulo anterior , aqui estão as funcionalidades relacionadas aos convidados que temos no aplicativo:

  1. Um convidado pode ver todos os artigos quando visitar /articles
  2. Um convidado pode ver um único artigo
  3. Um convidado pode ver um perfil de usuário
  4. Um convidado não pôde escrever um novo artigo e ser redirecionado para a página de inscrição
  5. Um convidado pode visitar e obter a página de inscrição
  6. Um convidado pode visitar e obter a página de login

Certifique-se de que o PHPUnit esteja funcionando corretamente com seu aplicativo

Antes de começarmos a escrever qualquer teste, vamos nos certificar de que o PHPUnit está funcionando corretamente com o seu aplicativo.

O binário do PHPUnit está incluído no vendor/bin/phpunit em seu projeto, então tudo que você precisa fazer é executá-lo (a partir do diretório do seu projeto).

Você deve ver este resultado:

Mesmo que ainda não tenhamos escrito nenhum teste, o Laravel inclui os seguintes testes de exemplo:

  • /tests/Feature/ExampleTest.php
  • /tests/Unit/ExampleTest.php

PS : Eu recomendo adicionar um apelido para o comando acima, então você não precisaria digitar vendor/bin/phpunit toda vez que quiser executar os testes

Por exemplo, estou usando esse alias:

alias lphpunit="vendor/bin/phpunit"

1 / Um convidado pode ver todos os artigos ao visitar /articles

Como vamos testar funcionalidades relacionadas ao ArticleController , vamos primeiro criar uma classe dedicada a esse controlador.

php artisan make:test ArticleControllerTest

Note que não passei o sinalizador --unit para o comando, o que significa que não estamos criando um teste de unidade, mas sim um teste de recurso . A classe recém-criada deve estar localizada em /tests/Feature/ArticleControllerTest.php

Você pode se livrar do testExample que foi incluído no ArticleControllerTest .

Vamos criar nossos primeiros testes.

Ao executar o PHPUnit, ele procurará todo o método público que começar com test ou que tenha @test em seu dockblock.

Então você pode usar este formato:

 public function testGuestCouldSeeListOfArticles() 
{
...
}

ou este aqui:

 /** 
* @test
*/
public function it_allows_anyone_to_see_list_all_articles()
{
...
}

Eu prefiro o segundo porque é muito mais fácil de ler.

Vou começar com o teste mais básico. Eu só quero ter certeza de que sempre que eu clicar na rota /articles , recebo uma página válida de volta.

 /** 
* @test
*/
public function it_allows_anyone_to_see_list_all_articles()
{
$response = $this->get(route('get_all_articles'));
$response->assertSuccessful();
}

Salve o arquivo e execute o PHPUnit (usando o vendor/bin/phpunit ou com o alias lphpunit que criamos anteriormente).

Nossos testes passaram

Note que, apesar de termos escrito apenas um teste e uma afirmação, o PHPUnit nos diz que temos 3 testes e 3 asserções.

Nota: Uma afirmação está testando uma única “coisa”, um teste poderia
contém várias asserções
. O teste que escrevemos acima contém apenas
asserção única
$response->assertSuccessful();

A razão por trás disso é que o PHPUnit executará todos os testes no diretório /tests . Executar todos os testes de cada vez não é um problema no estágio em que estamos agora, mas se você deseja executar apenas um único teste, pode passar o nome do teste (o nome do método) como um parâmetro para o teste. --filter flag como este:
lphpunit --filter=it_allows_anyone_to_see_list_all_articles

… E sim, como você deve ter adivinhado, você poderia adicionar um alias para este comando para economizar tempo na próxima vez que você quiser executar apenas um único teste.

 alias lphpunit="vendor/bin/phpunit" 
alias lphpunitf="lphpunit --filter="

lphpunitf it_allows_anyone_to_see_list_all_articles

Como você pode dizer, queremos testar mais do que apenas obter uma página válida, queremos ter certeza de que estamos obtendo a página correta.

Podemos testar isso com os seguintes passos:

  • certifique-se de que estamos obtendo a visão correta
  • Certifique-se de que a visualização contenha as variáveis necessárias para esta página

O Laravel fornece dois métodos para testar o acima:

 $response->assertViewIs('articles.index'); 
$response->assertViewHas('articles');

Nossa classe de teste deve se parecer com isso:

 <?php 
namespace TestsFeature;
 use TestsTestCase; 
use IlluminateFoundationTestingWithFaker;
use IlluminateFoundationTestingRefreshDatabase;
 class ArticleControllerTest extends TestCase 
{
/**
* @test
*/
public function it_allows_anyone_to_see_list_all_article()
{
$response = $this->get(route('get_all_articles'));
  $response->assertSuccessful(); 
$response->assertViewIs('articles.index');
$response->assertViewHas('articles');
}
 } 

Agora que podemos testar que estamos obtendo a visão correta (com a variável correta), não precisamos mais manter a primeira afirmação, pois está implícita.

2 / Um convidado pode ver um único artigo

Agora que testamos que um usuário convidado pode ver a lista de todos os artigos, vamos garantir que ela também exiba artigos individuais.

Para garantir que essa funcionalidade (mostrando artigos individuais para usuários convidados) funcione conforme o esperado, precisaríamos das seguintes etapas:

  1. Obter um artigo para ver (aleatoriamente)
  2. Gere a rota para este artigo e envie uma solicitação GET para ele
  3. Certifique-se de que estamos obtendo a visão correta (neste caso, articles.view )
  4. Certifique-se de que a exibição retornada contenha uma variável chamada $article
  5. Certifique-se de que estamos recebendo o artigo que queríamos acessar e não outro.

Nosso teste deve ser assim:

 /** 
* @test
*/
public function it_allows_anyone_to_see_individual_articles()
{
$article = Article::get()->random();
$response = $this->get(route('view_article', ['id' => $article->id]));

$response->assertViewIs('articles.view');
$response->assertViewHas('article');
$returnedArticle = $response->original->article;
$this->assertEquals($article->id, $returnedArticle->id, "The returned article is different from the one we requested");
}

Nota:
Nós podemos acessar a visão retornada porque a
variável $response->original

Você pode perguntar por que estamos fazendo todos esses passos para um recurso tão simples. O recurso é de fato simples e seus testes são simples também … simples, mas não triviais.

Estamos fazendo todos esses passos para garantir o seguinte:

  • Queremos testar acessar um artigo aleatório a cada vez (não queremos solicitar sempre o mesmo ID ou o mesmo artigo), porque podemos ter um problema no código que faz o aplicativo sempre retornar o mesmo artigo. Imagine, por exemplo, que em vez de procurar um artigo específico, por algum motivo nós atualizamos nosso código para usar Article::first() , não poderíamos detectar esse problema se continuarmos retornando o mesmo artigo (usando o mesmo ID) de novo e de novo.
  • É altamente recomendado usar rotas nos seus testes em vez de URLs, porque se você alterar a estrutura da sua URL, não precisará atualizar seus testes.
  • Também queremos ter certeza de que estamos de fato recebendo o artigo que estamos solicitando, porque podemos obter a visão correta que inclui a variável que estamos procurando, mas ela pode conter um artigo diferente daquele que solicitamos.

3 / Um convidado pode ver um perfil de usuário

Este deve ser muito parecido com o teste anterior, já que o conceito é o mesmo (acessar um modelo e devolvê-lo), mas estamos acessando um usuário em vez de um artigo.
Como não estamos testando artigos aqui, devemos criar uma nova classe de teste primeiro:

php artisan make:test UserControllerTest

Então, tudo o que precisamos fazer é adicionar o seguinte teste:

 /** 
* @test
*/
public function it_allows_anyone_to_see_users_profiles()
{
$user = User::get()->random();
  $response = $this->get(route('show_user_profile', ['id' => $user->id])); 
  $response->assertViewIs('users.show'); 
$response->assertViewHas('user');
  $returnedUser = $response->original->user; 
  $this->assertEquals($user->id, $returnedUser->id, "The returned user is different from the one we requested"); 
}

4 / Um convidado não pôde escrever um novo artigo e foi redirecionado para a página de inscrição

Este (e os restantes neste capítulo) são muito mais simples, pois não requerem acesso ao DB.

Para testar essa funcionalidade, precisamos das seguintes etapas:

  • tentar acessar a rota create_new_article
  • testar se fomos redirecionados para a página de login [login ou página de inscrição?]

O teste deve ser assim:

 /** 
* @test
*/
public function it_prevent_non_logged_in_users_from_creating_new_articles()
{
$response = $this->get(route('create_new_article'));
$response->assertRedirect('login');
}

5. Um convidado pode visitar e obter a página de inscrição e 6. Um convidado pode visitar e obter a página de login

Esses dois testes são ainda mais simples de escrever, já que estamos apenas verificando que quando tentamos visitar a página de login e inscrição, obtemos páginas válidas. Como estamos usando o controlador de autenticação interno do Laravel, não precisaremos testar a autenticação por conta própria.

Nós precisaríamos de uma nova classe de teste para esses dois testes também. Poderíamos criar uma classe dedicada apenas para eles. Normalmente eu coloco todos os “testes de página” (ou seja, testes que garantem que estamos obtendo páginas válidas quando atingimos determinada URL) em um PagesControllerTest (especialmente se eu tiver um controlador chamado PagesController ); ou apenas crie uma classe de teste para o HomeController já que na maioria dos casos eu adiciono a lógica das páginas que eu testo para esta classe.

Os dois casos de teste devem ser assim:

 /** 
* @test
*/
public function it_returns_register_page()
{
$response = $this->get(route('register'));
$response->assertSuccessful();
}
 /** 
* @test
*/
public function it_returns_login_page()
{
$response = $this->get(route('login'));
$response->assertSuccessful();
}

Além disso, em vez de apenas verificar se estamos obtendo uma página válida (o que é mais do que suficiente nessa situação), também verificamos se estamos obtendo as visualizações corretas como esta:

 /** 
* @test
*/
public function it_returns_register_page()
{
$response = $this->get(route('register'));
$response->assertViewIs('auth.register');
}
 /** 
* @test
*/
public function it_returns_login_page()
{
$response = $this->get(route('login'));
$response->assertViewIs('auth.login');
}

Como os testes detectariam alterações graduais na base de código?

Como discutimos em um capítulo anterior, um objetivo para escrever testes é garantir que as funcionalidades do aplicativo continuem funcionando da maneira que pretendíamos quando as criamos.

Gostaria de mostrar apenas um exemplo rápido de como os testes nos informarão que estamos introduzindo um novo código que está alterando o comportamento do aplicativo (uma alteração urgente).

Vamos supor que depois de algumas semanas trabalhando nesta aplicação, decidimos por algum motivo atualizar o construtor do ArticleController partir disso:

 public function __construct() 
{
$this->middleware("can:manage,article")->only('edit', 'update', 'delete');
$this->middleware("auth")->only('create');
}

para isso:

 public function __construct() 
{
$this->middleware("can:manage,article")->only('edit', 'update', 'delete');
$this->middleware("auth");
}

a única mudança é excluir ->only('create') da segunda linha do construtor.

Isso pode acontecer por acidente (um colega de equipe não viu o valor de proteger apenas uma ação com esse middleware) ou foi feito intencionalmente para evitar que usuários convidados leiam artigos antes de entrar.

Se executarmos os testes, obteremos isso:

Antes de escrever qualquer teste do aplicativo, as chances são de que você não notaria a mudança antes de um tempo. Talvez a mudança seja implementada sem que ninguém perceba, já que você estaria usando seu aplicativo como usuário logado a maior parte do tempo, e você não pensaria que precisaria testar as funcionalidades dos hóspedes após cada pequena mudar para o aplicativo.

Mas com os testes, qualquer alteração de quebra será detectada imediatamente, sem a necessidade de testar o aplicativo manualmente. E se você tiver um CI (Integração Contínua) configurado com o seu projeto (exploraremos como configurá-lo posteriormente neste ebook), você nem conseguirá mesclar / implantar sem corrigir o problema primeiro.

Conclusão

Neste capítulo, exploramos as diferentes etapas necessárias para testar as funcionalidades dos usuários convidados. Vimos que, embora os testes sejam simples, às vezes exigem etapas adicionais para garantir que estamos testando a coisa certa, e não estamos perdendo alguns casos extremos (especialmente quando introduzimos uma alteração que pode interromper o aplicativo). Também vimos como os testes detectariam as alterações de quebra na base de código.

Nos próximos capítulos, exploraremos testes relacionados a usuários logados, o que poderia ser um pouco mais desafiador do que os testes que vimos até agora.

Se você quiser acompanhar e ser notificado de qualquer progresso com este ebook (novos capítulos gratuitos, por exemplo), inscreva-se aqui: https://laraveltesting101.com/