외부 API를 호출하는 과정에서 Timeout을 어떻게 테스트할지에 대해 학습하며 작성된 글입니다. 학습 과정에서 작성된 글이기 때문에 잘못된 내용이 있을 수 있으며, 이에 대한 지적이나 피드백은 언제든 환영입니다.
1. 외부 API와 Timeout
깃허브를 통해 회원가입하는 시나리오를 살펴보겠습니다. 사용자가 회원가입 요청(1)을 보내면 API Gateway(2)를 지나 API 서버(3)로 접근하게 됩니다. API 서버는 깃허브 서버로 요청을 보내 액세스 토큰을 받아오고(4), 해당 액세스 토큰으로 사용자 정보를 요청(5)합니다. 마지막으로 깃허브로 부터 받아온 사용자 정보를 데이터베이스에 저장(6)한 후 응답을 반환합니다.
이때 외부 API를 호출하다보면 종종 Timeout이 발생해서 오류가 발생하게 됩니다. 즉 파란색 대역에서 지연이 발생해 통신이 종료되는 것입니다. 이는 개발자가 코드 레벨에서 컨트롤할 수 있는 영역이 아닙니다.
코드 레벨에서 살펴보면 아래와 같습니다. 하지만 Timeout은 재현하기도 힘들고, 운 좋게 재현 되더라도 반복적으로 테스트할 순 없습니다. 이는 어떻게 테스트 해야 할까요?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component
public class GoogleOAuth2Client implements OAuth2Client<GoogleMemberProfileResponse> {
......
private GoogleOAuth2AuthorizationResponse getAuthorization(
String code,
ClientRegistration clientRegistration
) {
try {
return webClient.post()
.accept(APPLICATION_JSON)
.bodyValue(new GoogleAuthorizationRequest(code, clientRegistration))
.retrieve()
.bodyToMono(GoogleOAuth2AuthorizationResponse.class)
.block();
} catch (WebClientResponseException | HttpClientErrorException.BadRequest e) {
throw BusinessException.of(API_CALLBACK_EXCEPTION);
} catch (Exception e) {
// Timeout 발생
throw BusinessException.of(TIMEOUT_EXCEPTION);
}
}
......
2. Wiremock
방법 자체는 간단합니다. 이전 글에서 Wiremock을 사용해 외부 API 대역을 스터빙 하는 방법에 대해 살펴보았는데요, 여기서 하나의 설정만 더해주면 됩니다. 아래와 같이 withFixedDelay를 지정해주면 데이터를 받아오는 과정에서 일정 시간만큼 딜레이를 시킬 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class OAuth2LoginTimeoutStub {
public static void setUP() throws IOException {
stubFor(post(urlEqualTo("/api/oauth2/google/authorization"))
.withRequestBody(equalToJson(getResource("__files/google-oauth2-requestbody.json")))
.willReturn(
WireMock.aResponse()
.withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.withStatus(200)
.withBody(getMockResponseByPath("__files/authorization-response.json"))
// Timeout
.withFixedDelay(6000)
)
);
stubFor(get(urlEqualTo("/api/oauth2/google/userprofile"))
.withHeader(AUTHORIZATION, equalTo(getAccessToken()))
.willReturn(WireMock.aResponse()
.withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.withStatus(200)
.withBody(getMockResponseByPath("__files/userprofile-response.json"))));
}
......
}
고정된 시간 뿐 아니라 아래와 같이 랜덤 딜레이, 일정 간격으로 딜레이를 줄 수 있습니다.
3. 정리
외부 API와 통신하는 과정에서 연결이 끊긴 경우는 사실상 테스트가 불가능 합니다. 이를 재현할 수가 없기 때문이죠. 하지만 Wiremock을 사용하면 간편한 설정으로 Timeout을 테스트할 수 있습니다.