Flask 앱 테스트하기 — pytest와 테스트 클라이언트
Flask 앱 테스트하기 — pytest와 테스트 클라이언트
Flask 앱을 만들면 "문서대로 잘 동작할까?"를 확인하고 싶어져요. Flask는 이를 위해 애플리케이션 테스트 유틸리티를 제공해요. 실제 서버를 띄우지 않고도 요청을 보내고 응답을 검사할 수 있는 테스트 클라이언트를 쓰는 것이 핵심이에요.
Flask는 애플리케이션을 테스트하는 유틸리티를 제공해요. 여기서는 테스트 셋업과 실행에 pytest 프레임워크를 써요.
$ pip install pytest
Fixtures
pytest의 fixture는 테스트 사이에서 재사용 가능한 코드 조각을 미리 만들어 두는 기능이에요. 앱·테스트 클라이언트·CLI 러너를 위한 fixture가 아래와 같이 tests/conftest.py에 들어갈 수 있어요.
애플리케이션 팩토리(app factory)를 쓴다면 app fixture를 정의해 앱 인스턴스를 만들고 설정해요. yield 앞뒤에 코드를 넣어 데이터베이스 생성·정리 같은 다른 리소스의 셋업·티어다운을 처리할 수 있어요.
import pytest
from my_project import create_app
@pytest.fixture()
def app():
app = create_app()
app.config.update({
"TESTING": True,
})
# other setup can go here
yield app
# clean up / reset resources here
@pytest.fixture()
def client(app):
return app.test_client()
@pytest.fixture()
def runner(app):
return app.test_cli_runner()
테스트 클라이언트로 요청 보내기
테스트 클라이언트는 실서버를 띄우지 않고 앱에 요청을 보내요. Flask의 클라이언트는 Werkzeug의 클라이언트를 확장한 것이에요. client.get()과 client.post()처럼 흔한 HTTP 요청 메서드에 대응하는 메서드가 있어요. 보통 path, query_string, headers, data 또는 json을 써요.
def test_request_example(client):
response = client.get("/posts")
assert b"<h2>Hello, World!</h2>" in response.data
응답은 TestResponse 객체로, 응답 객체의 모든 일반 프로퍼티를 가져요. 대개 response.data(뷰가 돌려준 바이트)를 살펴봐요. 텍스트가 필요하면 response.text나 response.get_data(as_text=True)를 써요.
쿼리 문자열을 설정하려면 딕셔너리를 query_string={"key":"value",...}로, 요청 헤더는 headers={}로 전달해요.
POST/PUT 요청 본문을 보내려면 data에 값을 전달해요. 원시 바이트를 넘기면 그대로 본문이 되고, 보통은 딕셔너리를 넘겨 폼 데이터를 설정해요. 값이 바이트 읽기 모드("rb")로 연 파일 객체라면 업로드 파일로 취급돼요. 파일 이름·콘텐츠 타입을 바꾸려면 (file, filename, content_type) 튜플을 전달해요.
애플리케이션·요청 컨텍스트
데이터베이스 확장은 대개 활성 앱 컨텍스트가 필요해요. with app.app_context():로 앱 컨텍스트를 밀어 넣으면 돼요.
def test_db_post_model(app):
with app.app_context():
post = db.session.query(Post).get(1)
요청 컨텍스트가 필요하면 with app.test_request_context():를 써요. 테스트 클라이언트의 요청 메서드와 같은 인자를 받아요.
def test_validate_user_edit(app):
with app.test_request_context("/user/2/edit", method="POST", data={"name": ""}):
# call a function that accesses `request`
messages = validate_edit_user()
assert messages["name"][0] == "Name cannot be empty."
테스트 요청 컨텍스트를 만드는 것은 Flask의 디스패칭 코드를 실행하지 않아서 before_request 함수가 호출되지 않아요. 그런 것들이 필요하면 보통 완전한 요청을 만드는 게 더 좋아요. 그래도 수동으로 호출할 수는 있어요.
정리하면, Flask 테스트는 app.test_client()로 실서버 없이 요청을 보내고, app.app_context()·app.test_request_context()로 컨텍스트가 필요한 코드를 검증하는 방식이에요. pytest의 fixture로 앱·클라이언트·러너를 재사용하면 깔끔하게 구성돼요.