Skip to content

Instantly share code, notes, and snippets.

@websterl3o
Created September 26, 2022 17:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save websterl3o/95cb922ec66cc08c9c43a74019c937bb to your computer and use it in GitHub Desktop.
Save websterl3o/95cb922ec66cc08c9c43a74019c937bb to your computer and use it in GitHub Desktop.
Como fazer um teste unitário

Pensei muito na melhor forma de fazer um teste unitário de uma funcionalidade "maior" e cheguei a uma ideia de como montar.

Suponto que eu estou trabalhando um serviço de consolidação de comissão, nesse serviço decidimos manter a consolidação de comissão como uma função que faz parte do serviço de comissão, mas para automatizar essa consolidação criamos um comando que executara de tempo em tempo que criara um Job que sera realizado para consolidar a comissão. 

Nessa comissão temos algumas configurações "payment_method", "blocked", "paid_at", "expected_date", "released_at", para criar o Job temos algumas regras, buscamos as comissões pelo metodo de "payment_method", que não esteja "blocked", que esteja "paid_at", que não esteja "released_at" e que a data "expected_date" seja menor ou igual a atual.

Esse job para consolidar a comissão precisa verificar se a comissão está "blocked", pois existem outros eventos que podem ocorrer que deixem a comissão bloqueado e comparar o dia atual com a data de pagamentos para verificarmos se deu a quantidade de dias que faltam para consolidação é igual a oque esperamos.

A função que realizara a consolidação, só precisa saber qual a comissão está sendo consolidada e o valor que vai ser consolidado (esse valor que vai ser consolidado existe porque em caso de antecipação possa haver taxas a serem cobradas em cima da comissão). Muito bem vamos a como testar isso tudo.

Sempre que vou testar algo procuro começar pelo último passo voltando até o ponto inicial, isso garante que meu teste percorreu cada parte e que o resultado seja mais assertivo. 

Nossa funcionalidade segue a seguinte hierarquia de ação:

-- Comando cria o job

---- Job de comissão chama a consolidação

------ Consolidação

Seguindo minha explicação anterior, eu começaria da função que realiza a consolidação.

Oque eu espero que aconteça nessa função?  Eu espero que ela crie um extrato para aquela comissão com o valor que eu passar, logo qualquer coisa diferente disso é um erro. Então vamos começando pelo erro, quando essa função pode dar erro?

public function test_nao_consolida_comissao_se_valor_for_string()
{
    $comissao = Comissao::factory()->create([
        'valor' => 1000
    ]);

    $initService = new ComissaoServico($comissao);

    $this->expectException(\Exception::class);
    $this->expectExceptionMessage('Valor deve ser um inteiro.');

    $valor = 'quintentos';
    $initService->consolidacao($valor);

    $this->assertDatabaseMissing('extrato', [
        'comissao_id' => $comissao->id,
    ]);

}

public function test_nao_consolida_comissao_se_valor_nao_for_passado()
{
    $comissao = Comissao::factory()->create([
        'valor' => 1000
    ]);

    $initService = new ComissaoServico($comissao);

    $this->expectException(\Exception::class);
    $this->expectExceptionMessage('Valor deve ser um inteiro.');

    $initService->consolidacao();

    $this->assertDatabaseMissing('extrato', [
        'comissao_id' => $comissao->id,
    ]);
}

public function test_consolida_comissao_com_mesmo_valor_da_comissao()
{
    $comissao = Comissao::factory()->create();
    $initService = new ComissaoServico($comissao);

    $initService->consolidacao($comissao->valor);
    
    $this->assertDatabaseHas('extrato', [
        'comissao_id' => $comissao->id,
        'valor_pago'  => $comissao->valor
    ]);
}

public function test_consolida_comissao_com_o_valor_diferente_da_comissao()
{
    $comissao = Comissao::factory()->create([
        'valor' => 1000
    ]);

    $initService = new ComissaoServico($comissao);

    $valor = 500;
    $initService->consolidacao($valor);
    
    $this->assertDatabaseMissing('extrato', [
        'comissao_id' => $comissao->id,
        'valor_pago'  => $comissao->valor
    ]);

    $this->assertDatabaseHas('extrato', [
        'comissao_id' => $comissao->id,
        'valor_pago'  => $valor
    ]);
}

Agora que testamos todos os casos que poderiam acontecer em um caso comum do uso da função de consolidação, passamos para o próximo nível.

Oque espero que aconteça quando o job rodar? Espero que ele crie um extrato com o valor da comissão, quando executado caso não esteja "blocked" e caso verificar que já deu o dia de realização da consolidação.

public function test_nao_consolida_comissao_por_comissao_blocked()
{
    $comissao = Comissao::factory()->create([
        'payment_method' => 'pix'
        'blocked' => true
        'paid_at' => now()->subDays(3)
    ]);

    $job = new ConsolidaComissaoJob($comissao);

    $job->handle();

    $this->assertDatabaseMissing('extrato', [
        'comissao_id' => $comissao->id,
        'valor'       => $comissao->valor
    ]);
}

public function test_nao_consolida_comissao_por_data_da_consolidacao_nao_atingida()
{
    $comissao = Comissao::factory()->create([
        'payment_method' => 'pix'
        'blocked' => false
        'paid_at' => now()
    ]);

    $job = new ConsolidaComissaoJob($comissao);

    $job->handle();

    $this->assertDatabaseMissing('extrato', [
        'comissao_id' => $comissao->id,
        'valor'       => $comissao->valor
    ]);
}

public function test_consolida_comissao()
{
    $comissao = Comissao::factory()->create([
        'payment_method' => 'pix'
        'blocked' => false
        'paid_at' => now()->subDays(3)
    ]);

    $job = new ConsolidaComissaoJob($comissao);

    $job->handle();

    $this->assertDatabaseHas('extrato', [
        'comissao_id' => $comissao->id,
        'valor'       => $comissao->valor
    ]);
}

Agora que o nível do job foi finalizado podemos seguir para o primeiro nível.

Oque espero que acontecer quando o comando rodar? Espero que ele busque por comissões que não esteja "blocked", que esteja "paid_at", que não esteja "released_at" e que a data "expected_date" seja menor ou igual a atual e crie um job para cada uma para realizar a consolidação.

public function test_nao_cria_jobs_com_todas_comissoes_bloqueadas()
{
    Bus::fake();

    Comissao::factory(5)->create([
        'blocked' => true
    ]);

    $this->artisan('consolidate-commissions');
    
    Bus::assertNotDispatched(ConsolidaComissaoJob::class);
}

public function test_nao_cria_jobs_com_todas_comissoes_sem_estarem_pagas()
{
    Bus::fake();

    Comissao::factory(5)->create([
        'paid_at' => null
    ]);

    $this->artisan('consolidate-commissions');

    Bus::assertNotDispatched(ConsolidaComissaoJob::class);
}

public function test_nao_cria_jobs_com_todas_comissoes_realizadas()
{
    Bus::fake();

    Comissao::factory(5)->create([
        'released_at' => now()->subDays(1)
    ]);

    $this->artisan('consolidate-commissions');

    Bus::assertNotDispatched(ConsolidaComissaoJob::class);
}

public function test_nao_cria_jobs_com_todas_comissoes_com_expected_date_maior_que_hoje()
{
    Bus::fake();

    Comissao::factory(5)->create([
        'expected_date' => now()->addDays(1)
    ]);

    $this->artisan('consolidate-commissions');

    Bus::assertNotDispatched(ConsolidaComissaoJob::class);
}

public function test_cria_jobs_de_consolidar()
{
    Bus::fake();

    Comissao::factory(5)->create([
        'blocked' => false,
        'paid_at' => now()->subDays(10),
        'released_at' => null,
        'expected_date' => now()->subDays(1)
    ]);

    $this->artisan('consolidate-commissions');

    Bus::assertDispatched(ConsolidaComissaoJob::class);
}
@iacoob
Copy link

iacoob commented Sep 26, 2022

Excelente Léo! ótima técnica para que o teste cubra todo o código e a aplicação fique completamente testada!

@Diegosny
Copy link

Muito bacana o artigo, parabéns!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment