Devops Integrated Tests - QuantumBitBR/API_5SEM GitHub Wiki

DevOps - Integration Tests

Visão Geral

Conceito

O teste de integração é um nível de teste de software que verifica a interação entre diferentes módulos, componentes ou sistemas para garantir que eles funcionem corretamente quando combinados.
Ele ocorre após os testes unitários (que avaliam partes individuais do código) e antes dos testes de sistema (que verificam o software como um todo).

Fluxo de Desenvolvimento de Testes de Integração

Para o bom desenvolvimento voltado para para testes de Integração, devemos destacar uma pessoa do time para atuar como QA, pois ele será o responsável por desenvolver a dinâmica dos testes. Este documento descreve as atividades do profissional de QA (Quality Assurance) ao longo de uma sprint em equipes ágeis, com foco em integração contínua de qualidade no desenvolvimento.


Antes da Sprint (Planejamento e Preparação)

1. Participa do planejamento da sprint (Sprint Planning)

  • Entende as histórias do backlog priorizadas.
  • Ajuda a definir critérios de aceitação claros com o Product Owner (PO).
  • Estima o esforço de testes em conjunto com os desenvolvedores.

2. Prepara casos de teste antecipadamente

  • Cria casos de teste com base nas histórias e nos critérios de aceitação.
  • Inicia a elaboração de testes manuais e automatizados antes mesmo da implementação.

Durante a Sprint (Execução e Integração)

3. Trabalha lado a lado com os desenvolvedores

  • Testa os incrementos à medida que são entregues (teste contínuo).
  • Participa de testes de integração e testes de API, se necessário.
  • Fornece feedback rápido — quanto mais cedo os problemas forem detectados, melhor.

4. Automatiza testes (quando possível)

  • Integra testes automatizados ao pipeline de CI/CD.
  • Automatiza testes funcionais .

5. Participa do Daily Scrum (reunião diária)

  • Reporta bugs encontrados, dúvidas sobre critérios, dependências e impedimentos.
  • Alinha com os desenvolvedores sobre funcionalidades prontas para teste.

Final da Sprint (Revisão e Retrospectiva)

6. Garante que todas as histórias tenham passado pelos testes antes da Review

  • Valida os critérios de aceitação.
  • Confirma se a Definition of Done (DoD) foi atendida.

7. Participa da Sprint Review (Revisão)

  • Pode demonstrar funcionalidades testadas.
  • Aponta possíveis limitações ou áreas que requerem atenção futura.

8. Participa da Retrospectiva

  • Colabora para melhorar o processo de testes e integração com o time.
  • Apresenta sugestões sobre qualidade, automação, ambiente e comunicação.

Relevância para o projeto

  • Confiança nas alterações de código.
  • Documentação viva da lógica implementada.
  • Detecção precoce de erros e regressões.
  • Facilidade para refatorações seguras.
  • Redução de falhas em produção.

Implementação

Stack de Testes Utilizada

  • JUnit 5 (@Test e suporte a testes Spring)
  • Spring Boot Test (para configuração do contexto Spring)
  • WebTestClient (para chamadas HTTP reativas)
  • AssertJ (para assertions avançadas)
  • Spring SQL Support (@Sql para scripts de banco de dados)
  • H2 Database (Banco de dados temporário para testar a integração)

Dependências no pom.xml

<dependencies>
    <!-- Spring Boot Starter Test (inclui JUnit 5, AssertJ, Mockito, etc.) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- WebFlux para WebTestClient -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- Banco de dados em memória para testes -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>


Exemplo Prático

package com.quantum.stratify.integracao;

import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.web.reactive.server.WebTestClient; import com.quantum.stratify.web.dtos.PercentualStatusUsuarioDTO; import com.quantum.stratify.web.dtos.TotalCardsDTO;

@AutoConfigureWebTestClient @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Sql(scripts = {"resources/sql/userStories/delete.sql", "resources/sql/userStories/insert.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = "resources/sql/userStories/delete.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) class UserStoryIT {

@Autowired
WebTestClient testClient;

@Test
void getTotalCards_status200(){
    TotalCardsDTO responseBody = testClient.get()
            .uri("/userStory/total-cards")
            .exchange()
            .expectStatus().isOk()
            .expectBody(TotalCardsDTO.class)
            .returnResult()
            .getResponseBody();

    org.assertj.core.api.Assertions.assertThat(responseBody).isNotNull();
    if (responseBody != null) {
        org.assertj.core.api.Assertions.assertThat(responseBody.quantidadeUserStories()).isEqualTo(1);
    }
}

@Test
void getPercentualStatus_status200(){
    List&lt;PercentualStatusUsuarioDTO&gt; responseBody = testClient.get()
            .uri("/userStory/percentual-por-status")
            .exchange()
            .expectStatus().isOk()
            .expectBodyList(PercentualStatusUsuarioDTO.class)
            .returnResult()
            .getResponseBody();

    org.assertj.core.api.Assertions.assertThat(responseBody).isNotNull();
    if (responseBody != null) {
        org.assertj.core.api.Assertions.assertThat(responseBody).hasSize(1);
    }
}

}


Regras Gerais para Testes de Integração

Configuração do Ambiente de Teste

  • @SpringBootTest deve ser usado para carregar o contexto completo da aplicação
  • WebEnvironment.RANDOM_PORT para evitar conflitos de porta em execução paralela
  • @Sql para gerenciamento do estado do banco de dados:
@Sql(scripts = {"cleanup.sql", "seed-data.sql"}, 
     executionPhase = BEFORE_TEST_METHOD)
@Sql(scripts = "cleanup.sql", 
     executionPhase = AFTER_TEST_METHOD)

Convenções

  • Nome dos métodos de teste deve ser descritivo e padronizado (português ou inglês):
    • deveCalcularTempoMedioPorProjeto()
    • shouldReturnProjectListByUserId()

Regras Gerais para Testes de Integração

  • Nome dos métodos de teste deve ser descritivo (em português ou inglês padronizado):
    • deveCalcularTempoMedioPorProjeto()
    • shouldReturnProjectListByUserId()
  • Evitar chamadas reais a banco de dados ou SMTP — apenas mocks.

Diagrama de Fluxo de um Teste de Integração

flowchart TD
    A[Início do Teste Unitário] --> B[Arrange: Configurações e Mocks]
    B --> C[Act: Execução do método testado]
    C --> D[Assert: Verificação dos resultados]
    D --> E{Resultado esperado?}
    E -- Sim --> F[Teste passa ✅]
    E -- Não --> G[Teste falha ❌]
    F --> H[Fim do Teste]
    G --> H

Exemplos de Implementação no Projeto

  • JwtAuthenticationIT.java
  • UserStoryIT.java

Benefícios para o Projeto

  • Maior confiança em releases.
  • Documentação viva da lógica implementada.
  • Facilidade na manutenção e expansão do código.
  • Redução de falhas em produção.

Observação Importante

Para regras de negócio mais complexas, crie um teste para cada cenário relevante, assegurando cobertura ampla dos comportamentos possíveis. Caso exija dúvidas sobre a criação de cenários relevantes, consultar o PO.


Testes de Integração no Frontend com Vitest

🔍 Por que executar esses testes?

  • As requisições aos serviços de backend sejam chamadas corretamente
  • Os parâmetros enviados estão completos e no formato esperado
  • A lógica de cada serviço esteja funcionando de forma isolada
  • A logica de integração deve ser mantida , utilizando de 2 ou mais serviços
  • Refatorar com segurança, sem medo de quebrar a integração frontend-backend

🛠️ Tecnologias Utilizadas

  • Vitest
  • vi.mock()

📁 Estrutura de Pastas Recomendadas

src/
├── services/
│   ├── AverageTimeService.ts
│   └── StatusService.ts

tests/ └── Integration/ └── Services/ ├── LifeTimeIntegration.spec.ts └── AverageTimeIntegration.spec.ts

🔪 Exemplos de Testes com Vitest

LifeTimeIntegration.spec.ts

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import lifeTimeService from '@/services/lifeTimeService'
import UserStoryService from '@/services/userStoryService'
import { api } from '@/services/apiConfig'

vi.mock('@/services/apiConfig', () => ({ api: { get: vi.fn() } }))

describe('Integração: LifeTimeService + UserStoryService', () => { beforeEach(() => { vi.clearAllMocks() })

it('deve buscar tempo médio e total de cards corretamente', async () => { // Mocks encadeados ;(api.get as any) .mockResolvedValueOnce({ data: { tempoMedio: 4.5 } }) // LifeTimeService .mockResolvedValueOnce({ data: { total: 12 } }) // UserStoryService

// Chamada para ambos os serviços
const tempo = await lifeTimeService.quantityPerProject(1, 2)
const total = await UserStoryService.getTotalCards(1, 2)

// Verificações
expect(api.get).toHaveBeenNthCalledWith(1, '/fatoeficiencia/projeto', {
  params: { projetoId: 1, usuarioId: 2 }
})

expect(api.get).toHaveBeenNthCalledWith(2, '/userStory/total-cards', {
  params: { idProjeto: 1, idUsuario: 2 }
})

expect(tempo.tempoMedio).toBe(4.5)
expect(total.total).toBe(12)

}) })

AverageTimeIntegration.spec.ts

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import AverageTimeService from '@/services/AverageTimeService'
import UserStoryService from '@/services/userStoryService'
import { api } from '@/services/apiConfig'

vi.mock('@/services/apiConfig', () => ({ api: { get: vi.fn() } }))

describe('Integração: AverageTimeService + UserStoryService', () => { beforeEach(() => { vi.clearAllMocks() })

it('deve buscar tempo médio e total de cards e calcular eficiência', async () => { // Simula resposta do tempo médio ;(api.get as any) .mockResolvedValueOnce({ data: { tempoMedio: 5 } }) // AverageTimeService .mockResolvedValueOnce({ data: { total: 20 } }) // UserStoryService

// Chamada para os dois serviços
const tempoMedio = await AverageTimeService.getAverageTime(1, 2)
const totalCards = await UserStoryService.getTotalCards(1, 2)

// Simulação de cálculo de eficiência (exemplo simples)
const eficiencia = totalCards.total / tempoMedio.tempoMedio

// Verificações
expect(api.get).toHaveBeenNthCalledWith(1, '/fatoeficiencia/media-tempo', {
  params: { projetoId: 1, usuarioId: 2 }
})

expect(api.get).toHaveBeenNthCalledWith(2, '/userStory/total-cards', {
  params: { idProjeto: 1, idUsuario: 2 }
})

expect(tempoMedio.tempoMedio).toBe(5)
expect(totalCards.total).toBe(20)
expect(eficiencia).toBe(4)

}) })

})

3. Adicione os scripts no package.json:

"scripts": {
  "test": "vitest",
  "test:ui": "vitest --ui"
}

4. Rode os testes:

npm install -D vitest @vitest/ui @vitejs/plugin-vue
npm install -D @vitest/coverage-v8
npm run test
⚠️ **GitHub.com Fallback** ⚠️