본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 3장: 통합 테스트와 API 테스트 자동화
2026년 3월 8일·AI / ML·

3장: 통합 테스트와 API 테스트 자동화

API 스키마 기반 테스트 자동 생성, 계약 테스트(Contract Testing), testcontainers와 AI를 결합한 데이터베이스 통합 테스트, 그리고 CI 파이프라인 통합 방법을 다룹니다.

18분952자8개 섹션
testingautomationquality-assuranceai
공유
ai-testing3 / 10
12345678910
이전2장: AI 기반 단위 테스트 자동 생성다음4장: E2E 테스트 -- AI 에이전트 기반 자동화

학습 목표

  • API 스키마에서 테스트를 자동 생성하는 원리를 이해합니다
  • 모킹 자동화와 계약 테스트의 차이를 파악합니다
  • testcontainers와 AI를 결합한 DB 통합 테스트를 실습합니다
  • CI 파이프라인에 통합 테스트를 효과적으로 통합하는 방법을 학습합니다

통합 테스트의 위치와 역할

Integration Test(통합 테스트)는 개별 컴포넌트가 올바르게 협력하는지를 검증합니다. 단위 테스트와 E2E 테스트 사이에 위치하며, 비용 대비 가장 높은 결함 검출률을 보이는 구간입니다.

AI가 통합 테스트에 기여하는 영역은 크게 세 가지입니다.

  1. API 스키마 기반 테스트 자동 생성 -- OpenAPI/GraphQL 스키마에서 테스트 케이스 도출
  2. 모킹 자동화 -- 외부 의존성의 모킹 코드를 자동 생성
  3. 계약 테스트 -- 서비스 간 인터페이스 규약의 자동 검증

API 스키마 기반 테스트 생성

OpenAPI에서 테스트 도출

OpenAPI(Swagger) 스키마는 API의 계약을 명확하게 정의합니다. AI는 이 스키마를 분석하여 모든 엔드포인트에 대한 테스트를 자동으로 생성할 수 있습니다.

openapi.yaml (일부)
yaml
paths:
  /api/users:
    post:
      summary: 사용자 생성
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, name, password]
              properties:
                email:
                  type: string
                  format: email
                name:
                  type: string
                  minLength: 2
                  maxLength: 50
                password:
                  type: string
                  minLength: 8
      responses:
        "201":
          description: 생성 성공
        "400":
          description: 유효성 검증 실패
        "409":
          description: 이메일 중복

이 스키마로부터 AI가 도출하는 테스트 시나리오는 다음과 같습니다.

users.api.test.ts (AI 생성)
typescript
import request from "supertest";
import { app } from "../app";
 
describe("POST /api/users", () => {
  describe("정상 요청", () => {
    it("유효한 데이터로 사용자를 생성합니다", async () => {
      const response = await request(app)
        .post("/api/users")
        .send({
          email: "test@example.com",
          name: "홍길동",
          password: "securePass123!",
        });
 
      expect(response.status).toBe(201);
      expect(response.body).toHaveProperty("id");
      expect(response.body.email).toBe("test@example.com");
    });
  });
 
  describe("유효성 검증", () => {
    it("이메일 형식이 올바르지 않으면 400을 반환합니다", async () => {
      const response = await request(app)
        .post("/api/users")
        .send({
          email: "invalid-email",
          name: "홍길동",
          password: "securePass123!",
        });
 
      expect(response.status).toBe(400);
    });
 
    it("이름이 2자 미만이면 400을 반환합니다", async () => {
      const response = await request(app)
        .post("/api/users")
        .send({
          email: "test@example.com",
          name: "홍",
          password: "securePass123!",
        });
 
      expect(response.status).toBe(400);
    });
 
    it("비밀번호가 8자 미만이면 400을 반환합니다", async () => {
      const response = await request(app)
        .post("/api/users")
        .send({
          email: "test@example.com",
          name: "홍길동",
          password: "short",
        });
 
      expect(response.status).toBe(400);
    });
 
    it("필수 필드가 누락되면 400을 반환합니다", async () => {
      const response = await request(app)
        .post("/api/users")
        .send({ email: "test@example.com" });
 
      expect(response.status).toBe(400);
    });
  });
 
  describe("비즈니스 규칙", () => {
    it("중복 이메일로 생성 시 409를 반환합니다", async () => {
      // 첫 번째 사용자 생성
      await request(app)
        .post("/api/users")
        .send({
          email: "duplicate@example.com",
          name: "사용자1",
          password: "securePass123!",
        });
 
      // 동일 이메일로 두 번째 생성 시도
      const response = await request(app)
        .post("/api/users")
        .send({
          email: "duplicate@example.com",
          name: "사용자2",
          password: "securePass456!",
        });
 
      expect(response.status).toBe(409);
    });
  });
});
Tip

AI는 스키마의 required, format, minLength, maxLength 등의 제약조건을 분석하여 경계값 테스트를 자동으로 생성합니다. 하지만 "중복 이메일" 같은 비즈니스 규칙은 스키마만으로는 파악이 어려우므로, 응답 코드의 의미를 기반으로 추론합니다.

GraphQL 스키마 기반 테스트

GraphQL 환경에서도 동일한 접근이 가능합니다. 스키마의 타입 정의와 리졸버 구조를 분석하여 쿼리/뮤테이션 테스트를 생성합니다.

schema.graphql (일부)
graphql
type Query {
  user(id: ID!): User
  users(page: Int = 1, limit: Int = 20): UserConnection!
}
 
type Mutation {
  createUser(input: CreateUserInput!): User!
}
 
input CreateUserInput {
  email: String!
  name: String!
  password: String!
}
users.graphql.test.ts (AI 생성)
typescript
import { createTestClient } from "apollo-server-testing";
import { server } from "../graphql-server";
 
const { query, mutate } = createTestClient(server);
 
describe("User GraphQL API", () => {
  it("ID로 사용자를 조회합니다", async () => {
    const GET_USER = `
      query GetUser($id: ID!) {
        user(id: $id) {
          id
          email
          name
        }
      }
    `;
 
    const result = await query({
      query: GET_USER,
      variables: { id: "user-1" },
    });
 
    expect(result.errors).toBeUndefined();
    expect(result.data?.user).toHaveProperty("email");
  });
 
  it("페이지네이션으로 사용자 목록을 조회합니다", async () => {
    const GET_USERS = `
      query GetUsers($page: Int, $limit: Int) {
        users(page: $page, limit: $limit) {
          edges { node { id email } }
          pageInfo { hasNextPage totalCount }
        }
      }
    `;
 
    const result = await query({
      query: GET_USERS,
      variables: { page: 1, limit: 10 },
    });
 
    expect(result.errors).toBeUndefined();
    expect(result.data?.users.edges).toBeInstanceOf(Array);
  });
});

모킹 자동화

통합 테스트에서 외부 의존성(결제 API, 이메일 서비스, 제3자 API 등)을 실제로 호출하면 테스트가 느려지고 비결정적이 됩니다. AI는 이러한 외부 의존성의 모킹 코드를 자동으로 생성할 수 있습니다.

AI 기반 모킹 생성 프로세스

payment-service.test.ts (AI 생성 모킹)
typescript
import { PaymentService } from "./payment-service";
import { PaymentGateway } from "./payment-gateway";
 
// AI가 PaymentGateway의 인터페이스를 분석하여 자동 생성한 모킹
jest.mock("./payment-gateway");
const mockGateway = jest.mocked(PaymentGateway);
 
describe("PaymentService", () => {
  let service: PaymentService;
 
  beforeEach(() => {
    service = new PaymentService(new PaymentGateway());
  });
 
  it("결제 성공 시 주문 상태를 완료로 변경합니다", async () => {
    mockGateway.prototype.charge.mockResolvedValue({
      transactionId: "txn-123",
      status: "succeeded",
      amount: 50000,
    });
 
    const result = await service.processPayment("order-1", 50000);
 
    expect(result.status).toBe("completed");
    expect(result.transactionId).toBe("txn-123");
  });
 
  it("결제 실패 시 적절한 에러를 반환합니다", async () => {
    mockGateway.prototype.charge.mockRejectedValue(
      new Error("카드 한도 초과")
    );
 
    await expect(service.processPayment("order-1", 5000000))
      .rejects.toThrow("카드 한도 초과");
  });
 
  it("타임아웃 시 재시도 후 실패합니다", async () => {
    mockGateway.prototype.charge
      .mockRejectedValueOnce(new Error("TIMEOUT"))
      .mockRejectedValueOnce(new Error("TIMEOUT"))
      .mockRejectedValueOnce(new Error("TIMEOUT"));
 
    await expect(service.processPayment("order-1", 50000))
      .rejects.toThrow("결제 처리 시간이 초과되었습니다");
    
    expect(mockGateway.prototype.charge).toHaveBeenCalledTimes(3);
  });
});
Info

AI 기반 모킹의 장점은 단순히 모킹 코드를 생성하는 것을 넘어, 실제 서비스의 응답 패턴(성공, 실패, 타임아웃, 네트워크 장애)을 포괄적으로 시뮬레이션한다는 점입니다. 이는 개발자가 놓치기 쉬운 장애 시나리오를 자동으로 테스트에 포함시킵니다.


계약 테스트(Contract Testing)

마이크로서비스 아키텍처에서 서비스 간 인터페이스의 호환성을 보장하는 것은 핵심 과제입니다. Contract Testing(계약 테스트)은 소비자(Consumer)와 제공자(Provider) 간의 약속을 코드로 정의하고 검증하는 방법입니다.

Consumer-Driven Contract 패턴

AI는 기존 API 호출 패턴을 분석하여 계약을 자동으로 생성할 수 있습니다.

user-consumer.pact.test.ts (AI 생성 계약 테스트)
typescript
import { PactV4, MatchersV3 } from "@pact-foundation/pact";
 
const { like, eachLike, string, integer } = MatchersV3;
 
const provider = new PactV4({
  consumer: "OrderService",
  provider: "UserService",
});
 
describe("UserService 계약", () => {
  it("활성 사용자 조회 계약", async () => {
    await provider
      .addInteraction()
      .given("활성 사용자가 존재하는 상태")
      .uponReceiving("사용자 조회 요청")
      .withRequest("GET", "/api/users/user-1")
      .willRespondWith(200, (builder) => {
        builder.jsonBody({
          id: string("user-1"),
          email: string("user@example.com"),
          name: string("홍길동"),
          isActive: like(true),
          createdAt: string("2026-01-01T00:00:00Z"),
        });
      })
      .executeTest(async (mockServer) => {
        const client = new UserClient(mockServer.url);
        const user = await client.getUser("user-1");
 
        expect(user.id).toBe("user-1");
        expect(user.isActive).toBe(true);
      });
  });
});

AI가 계약 테스트에 기여하는 방식

기여 영역설명
계약 자동 도출프로덕션 트래픽 분석으로 실제 사용 패턴 기반 계약 생성
호환성 분석스키마 변경 시 영향받는 소비자를 자동 파악
누락 계약 감지계약이 없는 서비스 간 통신을 찾아 경고
계약 업데이트API 변경 시 계약 파일 자동 업데이트 제안

testcontainers와 AI를 결합한 DB 통합 테스트

Testcontainers는 Docker 컨테이너를 활용하여 실제 데이터베이스, 메시지 큐, 캐시 등을 테스트 환경에서 구동하는 라이브러리입니다. AI와 결합하면 DB 스키마를 분석하여 의미 있는 통합 테스트를 자동 생성할 수 있습니다.

user-repository.integration.test.ts
typescript
import { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql";
import { Pool } from "pg";
import { UserRepository } from "./user-repository";
 
describe("UserRepository 통합 테스트", () => {
  let container: StartedPostgreSqlContainer;
  let pool: Pool;
  let repository: UserRepository;
 
  beforeAll(async () => {
    container = await new PostgreSqlContainer("postgres:16")
      .withDatabase("testdb")
      .start();
 
    pool = new Pool({
      connectionString: container.getConnectionUri(),
    });
 
    // 스키마 마이그레이션
    await pool.query(`
      CREATE TABLE users (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        email VARCHAR(255) UNIQUE NOT NULL,
        name VARCHAR(100) NOT NULL,
        is_active BOOLEAN DEFAULT true,
        created_at TIMESTAMP DEFAULT NOW()
      )
    `);
 
    repository = new UserRepository(pool);
  }, 60000);
 
  afterAll(async () => {
    await pool.end();
    await container.stop();
  });
 
  afterEach(async () => {
    await pool.query("DELETE FROM users");
  });
 
  it("사용자를 생성하고 조회합니다", async () => {
    const user = await repository.create({
      email: "test@example.com",
      name: "테스트 사용자",
    });
 
    const found = await repository.findById(user.id);
    expect(found).not.toBeNull();
    expect(found?.email).toBe("test@example.com");
  });
 
  it("중복 이메일 생성 시 에러를 반환합니다", async () => {
    await repository.create({
      email: "duplicate@example.com",
      name: "사용자1",
    });
 
    await expect(
      repository.create({
        email: "duplicate@example.com",
        name: "사용자2",
      })
    ).rejects.toThrow();
  });
 
  it("비활성 사용자를 필터링하여 조회합니다", async () => {
    await repository.create({ email: "active@example.com", name: "활성" });
    const inactive = await repository.create({
      email: "inactive@example.com",
      name: "비활성",
    });
    await repository.deactivate(inactive.id);
 
    const activeUsers = await repository.findActive();
    expect(activeUsers).toHaveLength(1);
    expect(activeUsers[0].email).toBe("active@example.com");
  });
});
Warning

testcontainers 기반 테스트는 Docker가 필요하며, 컨테이너 시작에 시간이 걸립니다. CI 환경에서는 컨테이너 이미지를 캐싱하고, 여러 테스트가 하나의 컨테이너를 공유하도록 설계해야 합니다. beforeAll에서 컨테이너를 시작하고 afterEach에서 데이터만 정리하는 패턴이 효과적입니다.


CI 파이프라인 통합

통합 테스트는 단위 테스트보다 실행 시간이 길므로, CI 파이프라인에서의 실행 전략이 중요합니다.

병렬 실행 전략

.github/workflows/integration-test.yml
yaml
name: Integration Tests
 
on:
  pull_request:
    branches: [main]
 
jobs:
  integration-tests:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        test-suite: [api, database, external]
    
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: testdb
          POSTGRES_PASSWORD: testpass
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
 
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: "pnpm"
      
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      
      - name: Run integration tests
        run: pnpm test:integration --shard=${{ matrix.test-suite }}
        env:
          DATABASE_URL: postgresql://postgres:testpass@localhost:5432/testdb

변경 영향 분석 기반 테스트 선택

AI를 활용하면 코드 변경 사항을 분석하여 실행이 필요한 통합 테스트만 선택적으로 실행할 수 있습니다. 이는 8장에서 더 깊이 다룹니다.

plaintext
코드 변경: src/services/payment.ts
  -> 영향 분석: PaymentService, OrderService
  -> 필요한 테스트: payment.integration.test.ts, order-payment.integration.test.ts
  -> 건너뛸 테스트: user.integration.test.ts, notification.integration.test.ts

정리

이 장에서는 AI를 활용한 통합 테스트와 API 테스트 자동화를 살펴보았습니다.

핵심 내용을 정리하면 다음과 같습니다.

  • OpenAPI/GraphQL 스키마에서 API 테스트를 자동 생성할 수 있으며, 유효성 검증과 비즈니스 규칙을 모두 포괄합니다
  • AI 기반 모킹은 정상/실패/타임아웃/장애 시나리오를 포괄적으로 생성합니다
  • 계약 테스트는 마이크로서비스 간 인터페이스 호환성을 보장하며, AI가 계약 도출과 업데이트를 자동화합니다
  • testcontainers와 AI를 결합하면 DB 스키마 기반의 현실적인 통합 테스트를 생성할 수 있습니다
  • CI 파이프라인에서는 병렬 실행과 변경 영향 분석을 통해 실행 시간을 최적화합니다

다음 장 미리보기

4장에서는 E2E 테스트로 범위를 확장합니다. 자연어로 테스트를 정의하는 Momentic, testRigor 같은 도구와, DOM 변경에 자동으로 적응하는 셀프 힐링 기능, 그리고 Playwright와 AI를 결합한 실전 E2E 테스트 자동화를 다룹니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#testing#automation#quality-assurance#ai

관련 글

AI / ML

4장: E2E 테스트 -- AI 에이전트 기반 자동화

자연어를 E2E 테스트로 변환하는 Momentic, testRigor, Functionize와 DOM 변경에 자동 적응하는 셀프 힐링 기능, Playwright와 AI를 결합한 실전 E2E 테스트 자동화를 다룹니다.

2026년 3월 10일·17분
AI / ML

2장: AI 기반 단위 테스트 자동 생성

LLM 기반 단위 테스트 자동 생성의 원리와 실전 활용법을 다룹니다. Diffblue, Codium/Qodo 도구를 활용한 pytest/Jest 테스트 생성 실습과 생성된 테스트의 품질 검증 방법을 안내합니다.

2026년 3월 6일·19분
AI / ML

5장: 변이 테스트(Mutation Testing)

변이 테스트의 원리와 변이 연산자를 이해하고, Stryker, PIT, mutmut 도구로 AI 생성 테스트의 품질을 검증하는 방법을 다룹니다. 변이 점수 측정과 비용-효과 분석도 포함합니다.

2026년 3월 12일·15분
이전 글2장: AI 기반 단위 테스트 자동 생성
다음 글4장: E2E 테스트 -- AI 에이전트 기반 자동화

댓글

목차

약 18분 남음
  • 학습 목표
  • 통합 테스트의 위치와 역할
  • API 스키마 기반 테스트 생성
    • OpenAPI에서 테스트 도출
    • GraphQL 스키마 기반 테스트
  • 모킹 자동화
    • AI 기반 모킹 생성 프로세스
  • 계약 테스트(Contract Testing)
    • Consumer-Driven Contract 패턴
    • AI가 계약 테스트에 기여하는 방식
  • testcontainers와 AI를 결합한 DB 통합 테스트
  • CI 파이프라인 통합
    • 병렬 실행 전략
    • 변경 영향 분석 기반 테스트 선택
  • 정리
    • 다음 장 미리보기