Intro
이전에 schema 라이브러리를 이용해서 API Response 테스트를 개선하는 방법에 대해 소개했습니다.
그동안 schema를 사용하며 알게된 팁 몇 가지를 공유 합니다.
schema에 대한 소개와 기본 사용법은 아래 글에 정리해두었으니 참고해주세요.
Tips
1. Paginated Response 검증
DRF의 pagination을 활용하면 다음과 같은 형태의 Response를 반환합니다.
{
'count': 100,
'next': 'http://localhost:8000/users/?page=2',
'previous': None,
'results': [
{
'id': 1,
'age': 20,
'mobile': '010-1111-2222',
},
{
'id': 2,
'age': 20,
'mobile': '010-2222-3333',
},
]
}
Python
복사
보통 한 프로젝트 내에서 사용하는 pagination 형태는 일정합니다.
따라서 다음과 같은 헬퍼 함수를 만들어두면 paginated 된 응답을 쉽게 검증할 수 있습니다.
def paginated_schema(schema: Dict[str, Any]) -> Schema:
return Schema(
{
'count': int,
'next': Or(str, None),
'previous': Or(str, None),
'results': [schema],
}
)
Python
복사
아래와 같이 results에 해당하는 Dict를 생성하여 인자로 전달하면 됩니다.
def test_get_user_list(client, user):
client.force_authenticate(user=user)
url = resolve_url('user_list')
response = client.get(url)
schema = paginated_schema(
{
'id': int,
'age': int,
'mobile': str,
}
)
assert response.status_code == 200
assert schema.is_valid(response.json())
Python
복사
2. Date & Datetime 검증
schema를 이용해서 response를 검증 할 때, JSON으로 변경하여 검증합니다.
assert schema.is_valid(response.json())
Python
복사
문제는 JSON 값을 검증하기 때문에 date와 datetime이 str으로 바뀌게 됩니다.
날짜와 시간을 str으로 검증하기 위해서는 날짜와 시간 형식에 맞는 정규 표현식을 만들어 검증해야 합니다.
하지만 세밀하게 날짜를 검증하기 위해서는 정규 표현식이 너무 복잡해집니다.
예를 들어, 일(day)을 검증할 때 두 자리 숫자인지만 확인하면 2022-01-32을 걸러낼 수 없습니다.
연/월/시 검증 역시 같은 문제를 가지고 있고, 복잡한 조건을 정규 표현식으로 표현하기 매우 까다롭습니다.
불필요한 복잡함을 없애고 싶어서 datetime 모듈을 이용하였습니다.
strptime() 함수를 이용해 다음과 같은 헬퍼 인스턴스를 만들 수 있습니다.
from datetime import datetime
valid_date = And(Use(lambda s: datetime.strptime(s, '%Y-%m-%d').date()), date)
valid_datetime = And(Use(lambda s: datetime.strptime(s, '%Y-%m-%dT%H:%M:%S')), datetime)
Python
복사
사용법은 다음과 같습니다.
def test_get_user_profile(client, user):
client.force_authenticate(user=user)
url = resolve_url('user_profile')
response = client.get(url)
schema = Schema(
{
'id': int,
'joined_at': valid_datetime, # str 대신 사용
}
)
assert response.status_code == 200
assert schema.is_valid(response.json())
Python
복사
joined_at 값을 strptime() 함수를 이용해 datetime 객체로 변환한 뒤, datetime 타입을 확인합니다.
schema에서 And 클래스와 Use 클래스가 어떻게 동작하는지 알고 있다면 쉽게 이해할 수 있을 것입니다.
3. update schema
간혹 미리 생성해놓은 schema 객체의 특정 값을 변경하여 사용하고 싶은 경우가 있습니다.
이럴 땐 schema 객체 내부의 schema attribute를 이용하면 됩니다.
user_schema = Schema(
{
'id': int,
'name': str,
}
)
Python
복사
>>> type(user_schema.schema)
<class 'dict'>
>>> user_schema.schema
{'id': <class 'int'>, 'name': <class 'str'>}
Python
복사
schema attribute는 Dict이기 때문에 update() 메소드를 이용하여 수정할 수 있습니다.
user_schema.schema.update(
{
'mobile': str,
}
)
Python
복사
>>> user_schema.schema
{'id': <class 'int'>, 'name': <class 'str'>, 'mobile': <class 'str'>}
Python
복사
대신 주의할 점이 있습니다.
테스트를 병렬로 실행하고 있다면, 다른 곳에서 선언한 schema를 조작하는 건 race condition을 일으킵니다.
따라서 schema 내부 값을 수정해야 한다면 deepcopy()를 이용합시다.
import copy
user_schema_copy = copy.deepcopy(user_schema)
user_schema_copy.schema.update(
{
'address': str,
}
)
Python
복사
>>> user_schema_copy.schema
{'id': <class 'int'>, 'name': <class 'str'>, 'mobile': <class 'str'>, 'address': <class 'str'>}
Python
복사
또는 중복 되더라도 명시적으로 다시 선언하는 것이 더 나을 수도 있습니다.