itdoc 인터페이스
이 페이지에서는 itdoc
의 인터페이스에 대해 포괄적으로 설명합니다.
describeAPI()
API 엔드포인트를 정의하고 문서화하는 주요 함수입니다.
시그니처:
function describeAPI(
method: HttpMethod,
path: string,
options: ApiDocOptions,
app: Express.Application | any,
testCallback: (apiDoc: RootBuilder) => void,
): void
예시:
describeAPI(
HttpMethod.GET,
"/users",
{
summary: "모든 사용자 조회",
tag: "사용자",
description: "시스템의 모든 사용자 목록을 반환합니다",
},
app,
(apiDoc) => {
// 여기에 테스트 케이스를 정의합니다.
},
)
매개변수 설명
method
API의 HTTP 메소드입니다.
값 예시는 여기를 참조하세요.
path
API 엔드포인트의 URL 경로입니다.
예시: /users
만일 PathVariable
을 포함하는 경로라면, 예시와 같이 정의하고 req()에서 pathParam()
으로 값을 설정하세요.
예시: /users/{id}
options
API 문서화에 필요한 요약, 태그, 설명을 포함한 구성 옵션입니다.
옵션 | 설명 | 예시 값 | 필수 여부 |
---|---|---|---|
summary | API의 간략한 요약 설명 | "사용자 등록 API" | x |
tag | API를 그룹화하기 위한 태그 | "사용자" | x |
description | API에 대한 자세한 설명 | "시스템에 등록된 모든 사용자 목록을 반환합니다." | x |
app
테스트할 Express 애플리케이션 인스턴스입니다.
itdoc
는 supertest를 사용하여 HTTP 요청을 시뮬레이션합니다. 따라서 app
매개변수는 supertest와 호환되는 Express 인스턴스여야 합니다.
예시:
-
app.js
import express from "express";
const app = express()
app.use(express.json())
app.post("/signup", function (req, res) {
const { username, password } = req.body
if (!username || !password) {
return res.status(400).json({
error: "username and password are required",
})
}
if (password.length < 8) {
return res.status(400).json({
error: "password must be at least 8 characters",
})
}
// (회원 가입 코드는 생략)
return res.status(201).json()
})
export default app -
테스트 코드
import app from "./app.js"
describeAPI(
HttpMethod.GET,
"/signup",
{
summary: "회원가입 API",
description: "아이디와 패스워드를 받아 회원가입을 수행합니다.",
},
app,
(apiDoc) => {},
)
testCallback
API 엔드포인트 테스트를 정의하는 콜백 함수입니다.
이 콜백 내부에서 itDoc()
을 사용하여 테스트 케이스를 작성합니다.
itDoc()
describeAPI
블록 내에서 개별 테스트 케이스를 정의합니다.
시그니처:
function itDoc(description: string, testFn: () => RootBuilder | Promise<RootBuilder>): void
매개변수:
description
: 테스트 케이스에 대한 설명입니다.testFn
: 실제 테스트 케이스 정의 함수입니다.
testFn
에서는 반드시 describeAPI에서 전달받은 apiDoc
객체를 반환하거나 실행해야 합니다.
예시:
itDoc("모든 사용자를 성공적으로 검색합니다", () => {
return apiDoc
.test()
.req()
.queryParam({ page: 1, limit: 10 })
.res()
.status(HttpStatus.OK)
.body({
users: field("사용자 목록", [{ id: 1, name: "John" }]),
total: field("총 사용자 수", 1),
});
});
apiDoc
테스트 요청 및 예상 응답을 설정하는 객체입니다.
무조건 test()
-> req()
-> res()
순서로 체이닝해야 합니다.
apiDoc
.test()
.req()
.body({...}) // 요청 본문
.header({...}) // 요청 헤더
.pathParam({...}) // 경로 매개변수
.queryParam({...}) // 쿼리 매개변수
.res()
.status(...) // 예상하는 응답 상태
.header({...}) // 예상하는 응답 헤더
.body({...}); // 예상하는 응답 본문
test()
모든 테스트 케이스에서 반드시 호출해야 합니다.
-
prettyPrint()
: 테스트의 request/response를 예쁘게 출력합니다.apiDoc
.test()
.prettyPrint()
.req()
...
req()
여기서 정의한 값이 API 요청에 사용됩니다.
body(body: object)
: 요청 본문 설정header(headers: object)
: 요청 헤더 설정pathParam(params: object)
: 경로 매개변수 설정queryParam(params: object)
: 쿼리 매개변수 설정expectStatus(status: HttpStatus)
: 예상 응답 상태 설정 (필수 호출)
res()
API 응답 속성을 정의합니다. 여기서 정의한 값과 실제 API 응답이 다르면, 테스트가 실패합니다.
status(status: HttpStatus)
: 예상 응답 상태 코드 설정- 여기서 사용되는
HttpStatus
는 여기에서 확인할 수 있습니다.
- 여기서 사용되는
body(body: object)
: 예상 응답 본문 설정header(headers: object)
: 예상 응답 헤더 설정
field()
요청 및 응답 본문의 필드를 문서화할 때 사용합니다.
시그니처:
function field<T>(description: string, value: T | ((val: any) => T)): T
매개변수:
description
: 필드 설명value
: 실제 값 또는 값 검증 함수
예시
고정된 값 사용
.res()
.body({
token: field("JWT 토큰", "eyJhbGciOi...")
})
콜백 사용 (응답 검증)
.res()
.body({
token: field("JWT 토큰", val => {
const jwtPattern = /^([A-Za-z0-9\-_]+)\.([A-Za-z0-9\-_]+)\.([A-Za-z0-9\-_]+)$/;
if (!jwtPattern.test(val)) throw new Error(`Invalid JWT token format: ${val}`);
return val;
}),
})
field()
를 생략하고 값을 직접 사용할 수 있습니다.
다만 이 경우엔 필드에 대한 설명이 문서화 되지 않으므로, 가능한 field()
를 사용하는 것이 좋습니다.
.req()
.body({ username: "john_doe" });
열거형
HttpMethod
HTTP 메소드를 나타내는 열거형입니다.
enum HttpMethod {
GET = "GET",
POST = "POST",
PUT = "PUT",
DELETE = "DELETE",
PATCH = "PATCH",
HEAD = "HEAD",
OPTIONS = "OPTIONS",
TRACE = "TRACE",
CONNECT = "CONNECT",
}
HttpStatus
HTTP 상태 코드를 나타내는 열거형입니다.
export enum HttpStatus {
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
EARLY_HINTS = 103,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
MULTI_STATUS = 207,
ALREADY_REPORTED = 208,
IM_USED = 226,
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
USE_PROXY = 305,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
PAYLOAD_TOO_LARGE = 413,
URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
IM_A_TEAPOT = 418,
MISDIRECTED_REQUEST = 421,
UNPROCESSABLE_ENTITY = 422,
LOCKED = 423,
FAILED_DEPENDENCY = 424,
TOO_EARLY = 425,
UPGRADE_REQUIRED = 426,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
VARIANT_ALSO_NEGOTIATES = 506,
INSUFFICIENT_STORAGE = 507,
LOOP_DETECTED = 508,
NOT_EXTENDED = 510,
NETWORK_AUTHENTICATION_REQUIRED = 511,
}