Diretrizes de Desenvolvimento
6. Testes

6. Testes

Testes são fundamentais para garantir a qualidade, confiabilidade e manutenibilidade de nosso software. Nossa estratégia de testes abrange vários níveis e tipos de testes, utilizando ferramentas modernas e práticas eficazes.

6.1. Tipos de testes

6.1.1. Testes Unitários

Testam unidades individuais de código (geralmente funções ou métodos) isoladamente.

Exemplo em Node.js (usando Japa):

import { test } from '@japa/runner'
import UserService from 'App/Services/UserService'
 
test.group('UserService', () => {
  test('should create a new user', async ({ assert }) => {
    const userService = new UserService()
    const user = await userService.create({ name: 'John Doe', email: 'john@example.com' })
    
    assert.equal(user.name, 'John Doe')
    assert.equal(user.email, 'john@example.com')
  })
})

Exemplo em PHP (Laravel):

use Tests\TestCase;
use App\Services\UserService;
 
class UserServiceTest extends TestCase
{
    public function test_it_creates_a_new_user()
    {
        $userService = new UserService();
        $user = $userService->create(['name' => 'John Doe', 'email' => 'john@example.com']);
        
        $this->assertEquals('John Doe', $user->name);
        $this->assertEquals('john@example.com', $user->email);
    }
}

6.1.2. Testes de Integração

Testam a interação entre diferentes partes do sistema.

Exemplo em Node.js (Japa):

import { test } from '@japa/runner'
import { createUser, getUserById } from 'App/Services/UserService'
 
test.group('User Integration', () => {
  test('should create and retrieve a user', async ({ assert }) => {
    const createdUser = await createUser({ name: 'Jane Doe', email: 'jane@example.com' })
    const retrievedUser = await getUserById(createdUser.id)
    
    assert.equal(retrievedUser.name, 'Jane Doe')
    assert.equal(retrievedUser.email, 'jane@example.com')
  })
})

Exemplo em PHP (Laravel):

use Tests\TestCase;
use App\Models\User;
 
class UserIntegrationTest extends TestCase
{
    public function test_it_creates_and_retrieves_a_user()
    {
        $user = User::factory()->create([
            'name' => 'Jane Doe',
            'email' => 'jane@example.com'
        ]);
 
        $response = $this->get("/api/users/{$user->id}");
 
        $response->assertStatus(200)
            ->assertJson([
                'name' => 'Jane Doe',
                'email' => 'jane@example.com'
            ]);
    }
}

6.1.3. Testes End-to-End (E2E)

Testam o fluxo completo da aplicação, simulando interações do usuário.

Importante: Todas as tarefas devem incluir testes E2E utilizando EndTest. Os desenvolvedores são responsáveis por criar e manter estes testes como parte do processo de desenvolvimento.

Exemplo de cenário de teste E2E no EndTest:

  1. Abrir a página de login
  2. Inserir credenciais válidas
  3. Clicar no botão de login
  4. Verificar se o usuário foi redirecionado para o dashboard
  5. Criar um novo item
  6. Verificar se o item aparece na lista
  7. Fazer logout

6.2. Frameworks e ferramentas de teste

  • Node.js: Japa para testes unitários e de integração
  • PHP (Laravel): PHPUnit (integrado ao Laravel)
  • E2E: EndTest
  • Cobertura de código: Codecov

6.3. Cobertura de código

Utilizamos o Codecov para monitorar a cobertura de código de nossos testes.

Diretrizes de cobertura:

  • Mínimo de 80% de cobertura para código novo
  • Manter ou aumentar a cobertura geral do projeto

Configuração do Codecov (exemplo para .codecov.yml):

coverage:
  status:
    project:
      default:
        target: 80%
    patch:
      default:
        target: 80%

6.4. Testes de performance

Realizamos testes de performance para garantir que nossa aplicação atenda aos requisitos de desempenho sob carga.

Ferramentas:

  • Apache JMeter para testes de carga
  • Lighthouse para performance de frontend

Exemplo de cenário de teste de performance:

  1. Simular 1000 usuários acessando a página inicial simultaneamente
  2. Verificar o tempo de resposta médio (deve ser < 2 segundos)
  3. Monitorar o uso de CPU e memória do servidor

6.5. Testes de segurança

Realizamos testes de segurança regularmente para identificar e corrigir vulnerabilidades.

Ferramentas e práticas:

  • OWASP ZAP para testes de segurança automatizados
  • Análise estática de código com SonarQube
  • Testes de penetração manuais trimestrais

Exemplo de teste de segurança automatizado:

# Executar OWASP ZAP via linha de comando
zap-cli quick-scan --self-contained --start-options "-config api.disablekey=true" https://nossa-aplicacao.com

Boas práticas adicionais:

  1. TDD (Test-Driven Development): Encorajamos a prática de TDD, escrevendo testes antes da implementação.

  2. Testes de regressão: Executar todos os testes antes de cada release para garantir que mudanças não quebraram funcionalidades existentes.

  3. Mocks e Stubs: Utilizar mocks para isolar unidades de código em testes unitários.

  4. Testes de API: Garantir que todas as endpoints da API estejam cobertas por testes.

  5. Testes de UI: Incluir testes de interface do usuário para garantir a correta renderização e funcionamento dos componentes.

  6. Continuous Testing: Integrar a execução de testes no pipeline de CI/CD.

Processo de revisão de testes:

  1. Os revisores de código devem verificar se os testes apropriados foram adicionados ou atualizados com cada PR.
  2. PRs não devem ser aprovados se não incluírem testes adequados.
  3. A cobertura de código deve ser revisada no Codecov antes da aprovação do PR.
  4. Para funcionalidades significativas, os testes E2E no EndTest devem ser revisados e aprovados.

Lembre-se: Testes não são apenas uma etapa no processo de desenvolvimento, mas uma parte integral do nosso compromisso com a qualidade. Cada desenvolvedor é responsável por garantir que seu código seja adequadamente testado em todos os níveis antes de solicitar uma revisão.