pytest-test-categories v1.1.0 발표: Google 테스트 철학을 파이썬에 도입합니다

발행: (2025년 12월 5일 오전 08:38 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

대부분의 테스트 스위트가 가진 문제점

솔직히 말해 보세요 – 아래 항목 중 얼마나 많은 것이 여러분의 코드베이스에 해당하나요?

  • ⏱️ 테스트에 시간 예산이 없어 CI 파이프라인이 느려짐
  • 🎲 네트워크 타임아웃, 레이스 컨디션, 공유 상태 등으로 인한 불안정한 테스트
  • 🔺 너무 많은 느린 통합 테스트로 인해 역전된 테스트 피라미드
  • 🚫 유닛, 통합, 시스템 테스트 사이에 강제된 경계가 없음

이 중 하나라도 끄덕였다면, 혼자가 아닙니다. 이것이 파이썬 프로젝트에서 가장 흔한 테스트 안티패턴입니다.

소개합니다 pytest-test-categories v1.1.0

Google의 검증된 테스트 철학(Software Engineering at Google)을 파이썬에 가져다 주는 pytest 플러그인입니다.

pip install pytest-test-categories

1. 명확한 리소스 제한으로 테스트를 크기로 구분하기

import pytest
import requests

@pytest.mark.small
def test_pure_function():
    """Must complete in 0"""

@pytest.mark.large
def test_external_api():
    """Full network access, up to 15 minutes"""
    response = requests.get("https://api.example.com")
    assert response.ok

@pytest.mark.xlarge
def test_extended_e2e():
    """Full access, up to 15 minutes, for extensive E2E tests"""
    # Long‑running end‑to‑end workflow
    pass

2. 헤르메틱(격리) 강제

======================================================================
[TC001] 네트워크 위반
======================================================================
Category: SMALL

무슨 일이 발생했나요:
  SMALL 테스트가 api.example.com:443에 네트워크 연결을 시도했습니다

해결 방법 (하나 선택):
  • responses, httpretty, 또는 respx를 사용해 네트워크 호출을 모킹하세요
  • 의존성 주입을 사용해 가짜 HTTP 클라이언트를 제공하세요
  • 테스트 카테고리를 @pytest.mark.medium 로 변경하세요
======================================================================

3. 테스트 피라미드 검증

======================== 테스트 크기 분포 ========================
Small:   120 테스트 (80.0%) - 목표: 80% ✓
Medium:   22 테스트 (14.7%) - 목표: 15% ✓
Large:     8 테스트 ( 5.3%) - 목표:  5% ✓
========================================================================

‘allow network’ 우회 옵션 없음

플러그인은 의도적으로 @pytest.mark.allow_network 마커를 제공하지 않습니다. 이런 마커를 추가하면 목적이 무색해집니다:

# This defeats the entire purpose
@pytest.mark.small
@pytest.mark.allow_network  # ❌ This marker doesn't exist!
def test_api():
    requests.get("https://api.example.com")  # Still flaky!

대신 적절한 카테고리를 사용하세요:

@pytest.mark.medium  # ✓ Honest about what the test does
def test_api():
    requests.get("https://api.example.com")

테스트 크기별 리소스 매트릭스

리소스SmallMediumLargeXLarge
시간1 초5 분15 분15 분
네트워크❌ 차단Localhost ✓✓ 허용✓ 허용
파일시스템❌ 차단✓ 허용✓ 허용✓ 허용
데이터베이스❌ 차단✓ 허용✓ 허용✓ 허용
서브프로세스❌ 차단✓ 허용✓ 허용✓ 허용
Sleep❌ 차단✓ 허용✓ 허용✓ 허용

작은 테스트에서 모킹하기

작은 테스트는 responses, respx, pytest-mock, pyfakefs, VCR.py 같은 모킹 라이브러리를 사용해도 실제 리소스에 도달하기 전에 라이브러리 레이어에서 가로채므로 위반이 발생하지 않습니다:

import pytest
import responses
import requests

@pytest.mark.small
@responses.activate
def test_api_with_mock():
    """This is hermetic – no real network call is made."""
    responses.add(
        responses.GET,
        "https://api.example.com/users",
        json={"users": []},
        status=200,
    )

    response = requests.get("https://api.example.com/users")
    assert response.json() == {"users": []}

파일시스템 작업을 작은 테스트에서 수행할 때는 pyfakefs 혹은 io.StringIO / io.BytesIO 같은 메모리 객체를 사용하세요.

점진적 강제 적용 롤아웃

첫날부터 엄격하게 적용할 필요는 없습니다:

# pyproject.toml

# Week 1: 탐색 – 어떤 것이 실패할지 확인
[tool.pytest.ini_options]
test_categories_enforcement = "off"

# Week 2‑4: 마이그레이션 – 위반을 점진적으로 수정
test_categories_enforcement = "warn"

# Week 5+: 강제 – 위반 시 빌드 실패
test_categories_enforcement = "strict"

병렬 실행 지원

pytest-xdist를 완전 지원하면서 카테고리별 테스트를 병렬로 실행하세요:

pytest -n auto

분산 통계는 워커 간에 올바르게 집계되며, 타이머 격리로 레이스 컨디션을 방지합니다.

머신 가독성 보고서 내보내기

대시보드와 CI 연동을 위한 JSON 보고서를 생성합니다:

pytest --test-size-report=json --test-size-report-file=report.json
{
  "summary": {
    "total_tests": 150,
    "distribution": {
      "small": { "count": 120, "percentage": 80.0 },
      "medium": { "count": 22, "percentage": 14.67 },
      "large": { "count": 8, "percentage": 5.33 }
    },
    "violations": {
      "timing": 0,
      "hermeticity": {
        "network": 0,
        "filesystem": 0,
        "subprocess": 0,
        "database": 0,
        "sleep": 0,
        "total": 0
      }
    }
  }
}

설치 및 빠른 시작

pip install pytest-test-categories

pyproject.toml에 강제 적용을 활성화하세요:

[tool.pytest.ini_options]
test_categories_enforcement = "warn"

리소스

Back to Blog

관련 글

더 보기 »