Testes automatizados para validações no Laravel

Thiago Alves • 01/07/2020

Seguindo o assunto abordado no último post, desta vez vou implementar outra variação de teste no Laravel.

Dias atrás, um desenvolvedor que nos acompanha aqui no blog, entrou em contato comigo para tirar algumas dúvidas sobre testes de validação de dados, compartilhei com ele alguns exemplos que escrevi na hora e discutimos mais alguns detalhes sobre o tema. Então, decidi que seria uma boa ideia postar os exemplos aqui, para que mais pessoas possam ter acesso.

Na minha rotina de trabalho, o desenvolvimento de APIs é algo bastante comum. Logo, escrever testes para isso, também, e uma das lógicas que costumo testar são exatamente as validações dos dados recebidos nas requisições.

Sem mais delongas, vamos ao que interessa!

O cenário

O meu sistema possui um cadastro de usuários simples. Poderia ser qualquer site desses em que criamos uma conta, preenchendo alguns campos básicos, inicialmente.

Recebo os dados através de uma Request, inserindo-os no banco e retornando o objeto criado (simplificado para o exemplo). Segue abaixo:

// app/Http/Controllers/UsersController.php

use App\Http\Requests\UserStoreRequest;
use App\User;

class UsersController extends Controller
{
    public function store(UserStoreRequest $request)
    {
        return User::query()
            ->create($request->validated());
    }
}

No FormRequest eu valido as informações vindas do formulário:

// app/Http/Requests/UserStoreRequest.php

use Illuminate\Foundation\Http\FormRequest;

class UserStoreRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'name'     => 'required|max:100',
            'email'    => 'required|email',
            'password' => 'required|confirmed',
        ];
    }
}

O teste

O meu objetivo é testar se os dados são validados pela Request corretamente. Para isso, escrevi dois testes onde, no primeiro o foco são os campos obrigatórios e no segundo são as especificidades de cada campo.

Veja abaixo:

// tests/Feature/Http/Controllers/UsersControllerTest.php

namespace Tests\Feature\Http\Controllers;

use Illuminate\Support\Str;
use Tests\TestCase;

class UsersControllerTest extends TestCase
{
    public function testStoreRequiredValidation()
    {
        $this
            ->sendStoreRequest()
            ->assertStatus(422)
            ->assertJsonStructure([
                'errors' => [
                    'name',
                    'email',
                    'password',
                ],
            ]);
    }

    public function testStoreSpecificValidation()
    {
        $this
            ->sendStoreRequest([
                'name'                  => Str::random(120),
                'email'                 => Str::random(50),
                'password'              => '123456',
                'password_confirmation' => '12345',
            ])
            ->assertStatus(422)
            ->assertJsonStructure([
                'errors' => [
                    'name',
                    'email',
                    'password',
                ],
            ]);
    }

    private function sendStoreRequest(array $data = [])
    {
        return $this->postJson(route('users.store'), $data);
    }
}

Veja o resultado:

Incrementando

No cenário descrito, também podemos validar se o e-mail informado já está cadastrado no banco. Basta adicionar a regra unique à minha Request.

// app/Http/Requests/UserStoreRequest.php

public function rules()
{
    return [
        'name'     => 'required|max:100',
        'email'    => 'required|email|unique:users',
        'password' => 'required|confirmed',
    ];
}

No teste, crio um usuário para garantir que haverá algum cadastrado e uso o seu e-mail na requisição para forçar o erro.

// tests/Feature/Http/Controllers/UsersControllerTest.php

public function testStoreUniqueEmailValidation()
{
    $user = factory(User::class)->create();

    $this
        ->sendStoreRequest([
            'name'                  => Str::random(100),
            'email'                 => $user->email,
            'password'              => '123456',
            'password_confirmation' => '123456',
        ])
        ->assertStatus(422)
        ->assertJsonStructure([
            'errors' => [
                'email',
            ],
        ]);
}

Veja o resultado:

Muitas regras podem ser implementadas nas validações de dados. Recomendo que, quando mais regras, mais testes sejam escritos, pois, acredito ser a melhor forma de explorar as possibilidades.

Concluindo

Para testes relacionados a comportamento de formulário, exibição de erros, recomendo o uso do Laravel Dusk. Inclusive, já fiz um post aqui no blog sobre isso.

Noto uma grande carência de conteúdo gratuito relacionado a testes, pelo menos em PHP. Ultimamente, muitas pessoas comentam comigo que não encontram nada muito prático e que se sentem perdidos, não sabendo por onde começar.

Gradualmente, trarei novos exemplos aqui para ajudar. Talvez possamos até fazer uma live, codando testes e tirando dúvidas. O que acha?

Nos vemos em breve!

Thiago Alves

Thiago Alves

Analista de Sistemas, no mercado de desenvolvimento de software desde 2011. Especialista em PHP, Laravel e Vue.js.

Comente abaixo o que você achou deste post, se ficou com alguma dúvida ou se gostaria de sugerir algum assunto.