응답을 반환하는 API 요청은 통과하는 테스트가 아닙니다. 그저 응답일 뿐입니다. 테스트는 응답이 올바른지 확인하는 무언가가 있을 때만 존재합니다. 이 확인이 바로 어설션(assertion)이며, 어설션의 품질이 테스트 스위트가 실제 버그를 잡을지 아니면 단순히 서버가 깨어있는지만 확인할지 결정합니다.
이 가이드는 API 어설션이 무엇인지, 작성할 가치가 있는 유형, 팀이 어설션에서 실수를 저지르는 부분, 그리고 Apidog에서 스크립트 없이 시각적으로 어설션을 구축하는 방법을 설명합니다.
API 어설션이란 무엇인가요?
어설션은 테스트가 통과하려면 참이어야 하는 응답에 대한 선언입니다. 요청을 보내면 API가 응답하고, 어설션은 해당 응답의 일부를 예상 값과 비교합니다. 일치하면 통과, 아니면 실패입니다.
어설션이 없으면 자동화된 테스트는 엔드포인트에 도달할 수 있다는 것만 증명합니다. 어설션이 있으면 엔드포인트가 *올바르다*는 것을 증명합니다. 이 둘 사이의 간격에 대부분의 프로덕션 사고가 발생합니다. 즉, API는 작동했지만 200 응답을 반환했고, 본문이 잘못된 경우입니다.
유용한 어설션은 구체적이고 독립적입니다. 구체적이라는 것은 실패 시 하나의 특정 지점을 가리킨다는 의미입니다. 독립적이라는 것은 다른 어설션이 먼저 통과하는 것에 묵시적으로 의존하지 않는다는 의미입니다. 단일 테스트 단계는 일반적으로 동일한 응답의 다른 측면을 확인하는 여러 어설션을 포함합니다.
상태 코드 어설션과 그것만으로는 충분하지 않은 이유
가장 일반적인 어설션은 HTTP 상태 코드를 확인합니다. 성공적인 읽기에는 200을, 생성된 리소스에는 201을, 잘못된 입력에는 400을, 인증 누락에는 401을 예상합니다. 이것은 필수적입니다. 상태 코드를 올바르게 처리하는 것은 그 자체로 하나의 분야입니다. API가 이 부분에서 일관성이 없다면 REST API가 사용해야 하는 HTTP 상태 코드를 읽어볼 가치가 있습니다.
하지만 상태 코드 어설션만으로는 약합니다. API는 빈 본문, 오래된 값, 객체가 있어야 할 곳에 null, 또는 성공으로 위장된 오류 메시지와 함께 200 OK를 반환할 수 있습니다. 상태 코드는 요청이 처리되었음을 나타냅니다. 데이터가 올바른지에 대해서는 아무것도 말해주지 않습니다.
상태 어설션을 테스트 단계의 첫 번째 줄로 취급하되, 결코 유일한 줄로 취급하지 마세요.
작성할 가치가 있는 어설션 유형
본문 내용 어설션. 응답의 실제 값을 확인합니다. id 필드가 존재하고 비어 있지 않습니다. email이 보낸 것과 일치합니다. total이 품목 합계와 같습니다. 이것들은 상태 코드가 놓치는 로직 버그를 잡아냅니다.
스키마 어설션. JSON 스키마 또는 OpenAPI 정의에 대해 응답의 *형태*를 검증합니다. 필수 필드가 존재하고, 유형이 올바르며, 예상치 못한 필드가 나타나지 않았습니다. 스키마 어설션은 백엔드가 필드를 문자열에서 객체로 조용히 변경하여 모든 클라이언트를 손상시키는 계약 변경(contract drift)을 잡아냅니다. 이것은 생산자와 소비자 전반에 걸쳐 아이디어를 공식화하는 API 계약 테스트와 겹칩니다.
헤더 어설션. Content-Type이 application/json인지, 캐싱 헤더가 의도한 대로 설정되었는지, CORS 헤더가 존재하는지, 그리고 Strict-Transport-Security와 같은 보안 헤더가 있는지 확인합니다.
응답 시간 어설션. 800ms와 같은 지연 예산을 설정하고, 응답이 느리면 테스트를 실패시킵니다. 성능 저하는 다른 모든 어설션 유형에서는 보이지 않으므로, 이는 기능 스위트에서 성능 저하를 잡아내는 유일한 유형입니다.
오류 형태 어설션. 부정적인 경우에 대해, 4xx 코드뿐만 아니라 오류 본문을 어설션합니다. error 필드가 validation_error와 같고, details 배열이 문제가 되는 필드의 이름을 지정하며, 메시지에 민감한 데이터가 유출되지 않습니다.
보안 어설션. 엔드포인트가 토큰 없는 요청을 거부하고, 만료된 토큰을 거부하며, 사용자 간의 권한 부여를 강제하고, 주입 페이로드를 이스케이프 처리되지 않은 채로 다시 반환하지 않는지 확인합니다.
상태, 두세 개의 본문 필드, 스키마, 그리고 응답 시간 예산을 어설션하는 테스트 단계는 실제로 작동하는 것입니다. 상태만 어설션하는 단계는 장식에 불과합니다.
어설션 로직이 잘못되는 부분
변동성이 큰 데이터에 대한 과도한 어설션. 정확한 created_at 타임스탬프 또는 생성된 UUID를 어설션하면 테스트가 아무 이유 없이 매번 실패합니다. 필드가 존재하고 올바른 유형을 가지고 있는지 어설션하되, 정확한 값은 어설션하지 마세요.
성공 경로에 대한 불충분한 어설션. 성공 경로 테스트는 상태 코드만 어설션할 가능성이 가장 높은 테스트입니다. 또한 사용자가 가장 많이 접하는 경로이기도 합니다. 가장 적은 어설션이 아니라 가장 철저한 어설션을 제공해야 합니다.
순서에 의존하는 어설션. 어설션 B가 어설션 A가 통과했을 때만 의미가 있는데 둘 다 맹목적으로 실행된다면, A의 실패는 B에서 혼란스러운 두 번째 실패를 초래합니다. 의존성이 명확하도록 단계를 구성하세요.
하나의 어설션이 두 가지 작업을 수행하는 경우. "응답이 올바르다"는 어설션이 아닙니다. 이를 분할하세요. 상태는 200이고, token이 존재하며, expires_in은 3600과 같습니다. 세 가지 확인, 세 가지 명확한 실패 메시지.
부정적인 경우를 무시하는 경우. 팀은 성공에 대해서는 엄격하게 어설션하지만, 실패에 대해서는 거의 하지 않습니다. 본문 어설션이 없는 부정적인 경우는 API가 "아니오"라고 말했음을 증명할 뿐, "올바르게" 아니오라고 말했음은 증명하지 않습니다.
Apidog에서 어설션 구축하기
Apidog에서 어설션은 시각적 테스트 빌더의 일부이므로 스크립팅 대신 클릭으로 정의합니다.
테스트 시나리오의 모든 요청에 대해 어설션 패널을 열고 다음 확인을 추가하세요.
- 상태 어설션. "응답 상태"를 선택하고
200과 같도록 설정합니다. - 본문 필드 어설션.
$.token과 같은 JSONPath 표현식을 사용하여 존재하고 비어 있지 않은 문자열인지 어설션합니다.$.expires_in이3600과 같은지 어설션합니다. Apidog는 응답 구조를 읽으므로, 경로를 맹목적으로 입력하는 대신 필드를 선택할 수 있습니다. - 스키마 어설션. 엔드포인트에 정의된 스키마에 대해 응답을 검증합니다. Apidog는 API 디자인과 테스트를 하나의 워크스페이스에 유지하므로, 어설션이 사용하는 스키마는 문서가 게시하는 스키마와 동일합니다. 변경될 수 있는 두 번째 사본이 없습니다.
- 응답 시간 어설션. 응답 시간이 예산보다 낮은지 확인하는 검사를 추가합니다.
- 필요할 때만 사용자 지정 스크립트. 시각적 확인으로 표현할 수 없는 로직의 경우, JavaScript 후처리기에 넣으세요. 대부분의 어설션은 이것이 필요하지 않습니다.
시나리오 전반에 걸쳐 어설션을 그룹화하면 Apidog가 이를 함께 실행합니다. 확장이 가능한 커버리지를 위해 데이터 파일을 첨부하여 하나의 어설션 세트가 데이터 기반 테스트 입력의 모든 행에 대해 실행되도록 합니다. 시나리오가 실행되면 생성된 보고서는 어떤 어설션이 어떤 요청에서 실패했는지, 예상 값과 실제 값을 나란히 보여줍니다.
동일한 시나리오가 CI에서 변경 없이 실행되므로 모든 커밋이 모든 어설션을 다시 확인합니다. CI/CD에서 API 테스트 자동화는 이러한 연결을 다룹니다. Apidog 다운로드하여 자신만의 엔드포인트에 대한 어설션 세트를 구축하세요.
예시 어설션 세트
사용자 객체를 반환하는 GET /users/{id}를 예로 들어 봅시다. 성공 경로를 위한 강력한 어설션 세트는 다음과 같습니다.
- 상태는
200과 같습니다. Content-Type헤더는application/json을 포함합니다.$.id는 요청된 id와 같습니다.$.email이 존재하고 이메일 패턴과 일치합니다.$.role은admin,member,viewer중 하나입니다.- 응답 본문은
User스키마를 따릅니다. - 응답 시간은 600ms 미만입니다.
알 수 없는 id를 가진 GET /users/{id}의 경우:
- 상태는
404와 같습니다. $.error는not_found와 같습니다.- 응답 본문에는
email,id또는 다른 사용자 필드가 포함되지 않습니다.
두 개의 요청, 열한 개의 어설션으로 계약, 데이터, 헤더, 성능 및 오류 동작을 확인했습니다. 이것이 릴리스를 보호하는 테스트 스위트와 단순히 서버를 핑하는 스위트를 구분하는 요소입니다.
CI 파이프라인의 어설션
어설션은 자동으로 실행될 때 완전한 가치를 발휘합니다. 누군가 일주일에 한 번 수동으로 실행하는 스위트는 버그를 일주일 늦게 발견합니다. CI에 연결된 동일한 스위트는 풀 리퀘스트 시점에 버그를 잡아냅니다.
어설션이 파이프라인에서 실행될 때 두 가지 설계 선택이 중요합니다. 첫째, 실패는 명확해야 합니다. "테스트 실패"라고만 표시된 CI 로그는 개발자가 로컬에서 실행을 재현하도록 강요합니다. "POST /auth/login에서 $.expires_in이 3600과 같아야 했으나 7200을 받았다"고 표시된 로그는 개발자가 즉시 찾아볼 곳을 알려줍니다. 강력하고 구체적인 어설션이 이러한 읽기 쉬운 실패를 만들어냅니다.
둘째, 어설션은 환경 전반에 걸쳐 안정적이어야 합니다. 프로덕션 사용자 ID를 하드코딩하는 어설션은 코드와 관련 없는 이유로 스테이징에서 실패할 것입니다. 환경별 값은 변수에 보관하고, 정확한 값이 환경에 따라 달라지는 구조 및 유형에 대해 어설션하세요. 스키마 어설션은 환경 간에 잘 이동하지만, 특정 생성된 ID에 대한 어설션은 그렇지 않습니다.
실용적인 패턴: 모든 엔드포인트에 대해 상태 및 스키마를 모든 곳에서 실행되는 기준선으로 어설션한 다음, 환경 인지 값 어설션을 그 위에 계층화합니다. 기준선은 어떤 환경에서도 계약 변경을 잡아내고, 값 어설션은 안정적인 데이터가 있는 곳에서 로직 버그를 잡아냅니다. 모든 커밋에서 둘 다 실행하면 스위트는 보고서가 아닌 게이트가 됩니다.
자주 묻는 질문
어설션과 테스트 케이스의 차이점은 무엇인가요? 테스트 케이스는 전체 확인입니다. 요청과 예상 결과가 결합된 것입니다. 어설션은 통과 또는 실패를 결정하는 개별 비교입니다. API 테스트 케이스 작성 방법을 참조하세요.
하나의 요청에는 얼마나 많은 어설션이 있어야 하나요? 상태, 주요 본문 필드, 스키마, 그리고 지연 예산을 다루기에 충분해야 합니다. 대부분의 엔드포인트의 경우 4~8개입니다. 각각 다른 것을 확인한다면 그 이상도 좋습니다.
정확한 응답 본문을 어설션해야 하나요? 안정적인 필드는 정확하게 어설션하고, 변동성이 큰 필드는 유형 또는 존재 여부로 어설션하세요. 타임스탬프 및 생성된 ID를 포함한 전체 본문을 어설션하면 매번 실패하는 테스트가 생성됩니다.
기능 테스트에서 API 성능을 어설션할 수 있나요? 예. 응답 시간 어설션은 일반적인 기능 실행 중에 지연 회귀를 잡아냅니다. 기본적인 예산 확인을 위해 별도의 부하 테스트가 필요하지 않습니다.
부정적인 테스트 케이스도 어설션을 가져야 하나요? 물론입니다. 그리고 그것이 가장 자주 빠뜨려지는 경우입니다. 상태 코드 확인만 있는 부정적인 경우는 API가 "아니오"라고 말했음을 증명할 뿐, "올바르게" 아니오라고 말했음은 증명하지 않습니다. 오류 필드, 문제가 되는 필드 세부 정보, 그리고 메시지에 민감한 데이터가 없는지를 어설션하세요.
사용자 지정 어설션 스크립트는 언제 사용해야 하나요? 시각적 빌더가 표현할 수 없는 로직(예: 요청 간 비교, 파생 값, 또는 이전 응답에 의존하는 조건부 확인)을 위해 스크립트를 남겨두세요. 상태, 스키마, 본문 필드, 타이밍과 같은 대부분의 어설션은 시각적 확인으로 더 깔끔하고 검토하기 쉽습니다.
