Devops Integrated Tests - QuantumBitBR/API_5SEM GitHub Wiki
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).
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.
- 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.
- 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.
- 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.
- Integra testes automatizados ao pipeline de CI/CD.
- Automatiza testes funcionais .
- Reporta bugs encontrados, dúvidas sobre critérios, dependências e impedimentos.
- Alinha com os desenvolvedores sobre funcionalidades prontas para teste.
- Valida os critérios de aceitação.
- Confirma se a Definition of Done (DoD) foi atendida.
- Pode demonstrar funcionalidades testadas.
- Aponta possíveis limitações ou áreas que requerem atenção futura.
- Colabora para melhorar o processo de testes e integração com o time.
- Apresenta sugestões sobre qualidade, automação, ambiente e comunicação.
- 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.
-
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)
<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>
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<PercentualStatusUsuarioDTO> 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);
}
}
}
-
@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)
- Nome dos métodos de teste deve ser descritivo e padronizado (português ou inglês):
deveCalcularTempoMedioPorProjeto()
shouldReturnProjectListByUserId()
- 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.
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
JwtAuthenticationIT.java
UserStoryIT.java
- 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.
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.
- 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
- Vitest
- vi.mock()
src/
├── services/
│ ├── AverageTimeService.ts
│ └── StatusService.ts
tests/
└── Integration/
└── Services/
├── LifeTimeIntegration.spec.ts
└── AverageTimeIntegration.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