Pytest

pytest는 파이썬을 위한 강력하고 유연한 테스트 프레임워크.
단위 테스트부터 기능 테스트까지 다양한 수준의 테스트를 지원하며, 개발자들 사이에서 높은 인기를 얻고 있다.

주요 특징

  1. 간결한 문법: pytest는 파이썬의 기본 assert 문을 사용하여 테스트를 수행한다. 이로 인해 테스트 코드가 매우 간결해진다.
  2. 자동 테스트 발견: ’test_‘로 시작하는 함수나 ‘Test’로 시작하는 클래스를 자동으로 테스트 대상으로 인식한다.
  3. 풍부한 플러그인 생태계: 다양한 플러그인을 통해 기능을 확장할 수 있다.
    주요 플러그인:
    - pytest-cov: 코드 커버리지 측정
    - pytest-mock: 목킹 기능 제공
    - pytest-xdist: 병렬 테스트 실행
    - pytest-django: Django 테스트 지원
  4. 상세한 실패 보고서: 테스트 실패 시 상세한 정보를 제공하여 디버깅을 용이하게 한다.
    • 실패한 테스트의 정확한 위치
    • 기대값과 실제값의 상세한 비교
    • 실행 시간 및 커버리지 정보
    • 성공/실패/스킵된 테스트 통계
  5. 매개변수화된 테스트: 여러 입력값에 대해 동일한 테스트를 반복 실행할 수 있다.

장점

  1. 간결성: unittest에 비해 더 간결한 문법을 제공한다.
  2. 유연성: 다양한 테스트 시나리오를 쉽게 구현할 수 있다.
  3. 확장성: 풍부한 플러그인 생태계를 통해 기능을 확장할 수 있다.
  4. 상세한 오류 보고: 테스트 실패 시 더 자세한 정보를 제공한다.

단점

  1. 학습 곡선: pytest만의 고유한 방식을 익혀야 한다.
  2. 기존 코드와의 호환성: 일부 기존 unittest 코드와 호환되지 않을 수 있다.

예외 테스트

pytest.raises 컨텍스트 매니저를 사용하여 예외 발생을 테스트할 수 있다.
예외의 종류뿐만 아니라 예외 메시지까지 검증할 수 있다.

기본 사용법

pytest를 사용하기 위해서는 먼저 설치해야 한다:

1
pip install pytest

간단한 테스트 예제:

1
2
3
4
5
6
# test_example.py
def test_addition():
    assert 1 + 1 == 2

def test_subtraction():
    assert 5 - 3 == 2

이 테스트를 실행하려면 터미널에서 다음 명령어를 입력한다:

1
pytest test_example.py

사용 예제

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# calculator.py
class Calculator:
    def add(self, a, b):
        return a + b
    
    def divide(self, a, b):
        if b == 0:
            raise ValueError("Cannot divide by zero")
        return a / b
    
    def multiply(self, a, b):
        return a * b

# test_calculator.py
import pytest
from calculator import Calculator

# 픽스처 정의
@pytest.fixture
def calculator():
    """테스트에서 사용할 Calculator 인스턴스를 제공하는 픽스처"""
    return Calculator()

# 기본 테스트
def test_add(calculator):
    assert calculator.add(1, 2) == 3
    assert calculator.add(-1, 1) == 0
    assert calculator.add(0, 0) == 0

# 파라미터화된 테스트
@pytest.mark.parametrize("a, b, expected", [
    (3, 2, 6),
    (0, 5, 0),
    (-2, -3, 6),
    (0, 0, 0)
])
def test_multiply(calculator, a, b, expected):
    """여러 입력값에 대한 곱셈 테스트"""
    assert calculator.multiply(a, b) == expected

# 예외 테스트
def test_divide_by_zero(calculator):
    """0으로 나누기 시 예외 발생 테스트"""
    with pytest.raises(ValueError) as exc_info:
        calculator.divide(1, 0)
    assert str(exc_info.value) == "Cannot divide by zero"

# 마킹을 사용한 테스트
@pytest.mark.slow
def test_complex_calculation(calculator):
    """시간이 오래 걸리는 복잡한 계산 테스트"""
    result = calculator.add(
        calculator.multiply(100, 100),
        calculator.divide(50, 2)
    )
    assert result == 10025

# 테스트 스킵
@pytest.mark.skip(reason="아직 구현되지 않은 기능")
def test_future_feature(calculator):
    pass

# 조건부 스킵
@pytest.mark.skipif(sys.version_info < (3, 8), 
                    reason="Python 3.8 이상 필요")
def test_new_feature(calculator):
    pass

# 클래스를 사용한 테스트 그룹화
class TestCalculatorAdvanced:
    def test_negative_numbers(self, calculator):
        assert calculator.add(-1, -1) == -2
        
    def test_floating_points(self, calculator):
        assert calculator.divide(5.0, 2.0) == 2.5

# 테스트 설정 및 정리
@pytest.fixture(scope="module")
def complex_setup():
    """모듈 레벨의 복잡한 설정"""
    print("\nSetting up resources…")
    yield "resource"
    print("\nCleaning up resources…")

def test_with_setup(complex_setup):
    assert complex_setup == "resource"

고급 기능

  1. Fixtures: 테스트 함수에 필요한 데이터나 객체를 제공한다.
    • 재사용 가능한 테스트 데이터 제공
    • 여러 범위(function, class, module, session) 지원
    • 자동 정리(cleanup) 기능
    • 의존성 주입 방식의 사용
1
2
3
4
5
6
7
8
import pytest

@pytest.fixture
def sample_data():
    return [1, 2, 3, 4, 5]

def test_sum(sample_data):
    assert sum(sample_data) == 15
  1. 매개변수화된 테스트:
    @pytest.mark.parametrize 데코레이터를 사용하여 하나의 테스트 함수로 여러 입력값을 테스트할 수 있다.
    이는 코드 중복을 줄이고 테스트 커버리지를 높이는데 매우 유용하다.
1
2
3
4
5
import pytest

@pytest.mark.parametrize("a, b, expected", [(1, 2, 3), (2, 3, 5), (3, 5, 8)])
def test_addition(a, b, expected):
    assert a + b == expected
  1. 마커:
    테스트에 메타데이터를 추가하여 특정 테스트만 실행하거나 건너뛸 수 있습니다.
1
2
3
4
5
6
import pytest

@pytest.mark.slow
def test_slow_function():
    # 시간이 오래 걸리는 테스트
    pass

모범 사례와 팁

pytest를 효과적으로 사용하기 위한 몇 가지 권장사항:

  1. 테스트 구조화
    • 명확한 이름 규칙 사용
    • 관련 테스트를 클래스로 그룹화
    • 적절한 픽스처 범위 선택
  2. 테스트 격리
    • 각 테스트는 독립적으로 실행 가능해야 함
    • 테스트 간 상태 공유 피하기
    • 적절한 setUp과 tearDown 사용
  3. 테스트 최적화
    • 느린 테스트 식별 및 최적화
    • 병렬 실행 활용
    • 적절한 테스트 건너뛰기 사용

참고 및 출처

pytest documentation