
검증 시나리오가 빈번하게 광고주의 요청으로 인해 변경될 때가 많았다.
그때마다 유닛 테스트를 활용하여 로직 수정으로 인한 side effect가 없었는지 확인한 후 pr을 올리는 습관을 들였다.
@pytest.mark.parametrize(
"box_coords, expected",
[
([(0, 0), (10, 0), (10, 5), (0, 5)], (0, 0, 10, 5)),
([(0.9, 1.2), (2.8, 8.7), (2.7, 3.4), (1.1, 0.5)], (0, 0, 2, 8)),
],
)
def test_get_bbox_bounds_normal(box_coords, expected):
assert get_bbox_bounds(box_coords) == expected
@pytest.mark.parametrize(
"box_coords, expected_exception",
[
# 1) 빈 리스트: min()/max()가 ValueError
([], ValueError),
# 2) None 등 비서술(인덱싱 불가): pt[0]에서 TypeError
([None], TypeError),
],
)
def test_get_bbox_bounds_invalid(box_coords, expected_exception):
with pytest.raises(expected_exception):
get_bbox_bounds(box_coords)
간단하게 설명을 덧붙이자면, pytest에서 제공하는 parametrize에 추측되는 시나리오를 나열한다.
테스트 코드 함수 내부에 예상되는 결과물을 assert로 비교해보면 유닛 테스트 코드 작성 끝!
[예외 발생 메세지 검사]
pytest.raises는 메서드 형태나 컨텍스트 관리자 형태로 호출하여, 예외 메세지 검사가 가능하다.
⇒ match 구문을 사용하기도 한다.
[테스트 시나리오 파라미터화]
pytest.mark.parameterize 데코레이터를 사용하면 테스트케이스를 파라미터화 할 수 있다.
특정 테스트 함수의 데이터 집합을 사용할 때는 pytest.mark.parameterize를 주로 사용했다.
[fixture]
공통으로 사용되는 객체를 제공하는 메커니즘으로 여러 객체를 생성하거나 데이터를 노출하는 것 외에도 직접 호출되지 않는 함수를 수정하거나 사용될 객체를 미리 설정하는 등의 사전 조건 설정에 사용된다.
텍스트 비교할 때가 있었는데, 기존 텍스트를 fixture로 선언해서 여러번 정의할 필요없게 테스트 코드를 구현했다.
[mocking]
만약에, 내부 함수가 들어있다면, mocking 처리로 단위 테스트가 가능케 만들어줬다.
# unittest.mock.patch 컨텍스트 매니저
with patch("your_module.your_function.requests.get") as mock_get_request:
# pytest fixture
monkeypatch
monkeypatch (pytest 제공 fixture)# app.py import os
def get_env_var(key):
return os.environ.get(key, "default")
# test_app.py
def test_get_env_var(monkeypatch):
monkeypatch.setenv("API_KEY", "test_key")
assert get_env_var("API_KEY") == "test_key"with patch (unittest.mock.patch)unittest.mock의 기능.with 구문) 또는 데코레이터로 사용된다.MagicMock 또는 원하는 값으로 설정 가능.from unittest.mock import patch
import app
def test_get_env_var():
with patch("app.os.environ.get", return_value="mocked_key"):
assert app.get_env_var("API_KEY") == "mocked_key"함수 리팩토링 당시 많은 도움을 줬던 테스트 방식이다.
회사 특성 상 여러 인턴들이 자기들만의 생각으로 하나의 서비스를 완성시키다 보니, 함수의 복잡성이 리팩토링 이전은 높았다.
통합 테스트를 작성해보면서, 함수를 정리해보는 시간을 가졌고 이를 토대로 리팩토링을 진행했다.
사내 시스템과 관련되다 보니 인프라를 맘대로 구축하거나 변경할 수 없는 환경이어서 진행해보진 못했다.
서비스를 출시할 때 기능 개발과 함께 테스트도 병행하는 작업이 꽤나 중요하다고 생각이 들었다.
학교 프로젝트 진행 시에는 코드 작성을 한 후에 테스트 코드 작성의 흐름을 따랐지만, TDD의 핵심은 기존에 테스트 코드를 작성한 후에 실제 코드를 작성하는 것이 맞다고 한다.
이렇게 하면, 개발자가 기능 구현 시 생각 정리를 할 수 있고 명확한 기능의 흐름을 구상할 수 있기에 추천하는 방식같다.
TDD는 크게 Red-Green-Blue 3가지 단계를 거친다고 한다:
1. Red: 실제 구현을 하기 전에, 먼저 실패하는 테스트 코드를 작성한다. 그 후 테스트를 실행한다. 실제 코드가 작성되지 않았기에 테스트 코드는 당연히 실패한다.
2. Green: 테스트를 통과하기 위해 가장 간단한 형태로 코드를 작성한다. 그 후 테스트를 실행한다. 테스트는 실제 구현이 되었기에 통과한다.
3. Blue: Green 단계의 코드를 더 좋은 형태로 리팩토링한다. 이 과정에서 지속적으로 테스트를 실행해서 테스트가 통과하는지 확인한다.
이러한 형태로 개발하게 되면 여러 가지 장점이 있다.
1. 코드의 동작이 명확해진다.
2. 구현을 잘못했을 경우 바로 확인이 가능하다.
3. 코드 작성 과정에서 확신을 얻을 수 있다.
서비스와 같이 사용자가 있는 환경에서는 새로운 기능 개발 시에 사이드 이펙트를 줄일 수 있는 좋은 개발 방식이라고 생각된다.
앞으로 프로젝트를 진행할 때 테스트 코드 작성을 습관화 하는 것이 중요할 것 같다고 생각이 들었다.