본문 바로가기

Programing/테스트

[삽질] Spock : ClientHttpRequestInterceptor 테스트 하기

Spock에서는 Mocking을 할 때 파라미터의 값 같은 조건을 제약해서 정할 수가 있다.

Reference: Argument Constrains


전임자가 ClientHttpRequestInterceptor를 구현하여 RestTemplate의 요청과 응답을 가로채서 로그를 찍는 코드가 있었다.


코드는 code.i-harness의 아래 글의 코드와 비슷하게 되어 있다.

옵션 2. 인터셉터 사용하기 > 인터셉터 구현

https://code.i-harness.com/ko-kr/q/79571a


문제는 RestTemplate의 응답을 하는 외부 API가 응답을 UTF-8로만 하지 않고 EUC-KR로 응답을 주는 경우가 있었다.

그래서 한글이 포함되어 있는 경우 로그가 깨져서 보였다.


문제를 해결할 방법은 간단했다. 컨텐츠의 타입의 캐릭터셋을 참고해서 인코딩을 선택하면 되기 때문이다.


삽질은 테스트 코드를 짜면서 생겨났다.

아래와 같은 Spock 테스트 코드(Groovy)이다.

def "intercept: UTF8 body test"() {
given:
final Charset charset = StandardCharsets.UTF_8
ClientHttpResponse response = mockRequestAndExecution(charset)

and:
byte[] body = "요청 BODY".getBytes(charset)

when:
ClientHttpResponse result = cut.intercept(request, body, execution)

then:
result == response
1 * cut.log.info(_ as String, _ as URI, _ as HttpMethod, _ as HttpHeaders, "요청 BODY")
1 * cut.log.info(_ as String, _ as HttpStatus, _ as String, _ as HttpHeaders, "응답 BODY")
}

첫 번째 요청에 대한 것은 매칭이 잘 되었는데, 응답에 대한 로그 호출이 자꾸 실패가 나는 것이었다.


분명 로그를 찍고 있기에 한 번씩 매칭이 되어야 하는데 아래와 같이 에러 메세지가 나오는 것이었다.

Too few invocations for:


1 * cut.log.info(_ as String, _ as HttpStatus, _ as String, _ as HttpHeaders, "응답 BODY")   (0 invocations)


Unmatched invocations (ordered by similarity):


1 * <Logger>.info('\n========== API RESPONSE BEGIN ==========\nSTATUS CODE : {}\nSTATUS TEXT : {}\nHEADERS     : {}\nPAYLOAD     : {}\n========== API RESPONSE END ==========\n', [<org.springframework.http.HttpStatus@78461bc4 value=200 reasonPhrase=OK name=OK ordinal=4>, <java.lang.String@9dc value=OK hash=2524>, <org.springframework.http.HttpHeaders$SpockMock$2018250511@1095f122 $spock_interceptor=org.spockframework.mock.runtime.JavaMockInterceptor@3d6300e8 headers=null readOnly=false>, <java.lang.String@d80c894c value=응답 BODY

 hash=-670267060>])




at org.spockframework.mock.runtime.InteractionScope.verifyInteractions(InteractionScope.java:93)


문제를 해결하고자 Spock의 인자 조건 중 클로저를 사용해보기로 하였다.

아래와 같이 테스트 코드를 바꾸었다.

def "intercept: UTF8 body test"() {
given:
final Charset charset = StandardCharsets.UTF_8
ClientHttpResponse response = mockRequestAndExecution(charset)

and:
byte[] body = "요청 BODY".getBytes(charset)

when:
ClientHttpResponse result = cut.intercept(request, body, execution)

then:
result == response
1 * cut.log.info(_ as String, _ as URI, _ as HttpMethod, _ as HttpHeaders, "요청 BODY")
0 * cut.log.info(_ as String, _ as HttpStatus, _ as String, _ as HttpHeaders, {
String str = '>' + it + '<'
print str
return str.size() > 3 && str.contains('BODY')
})
}

호출이 안된다고 하고 했는데 이번에는 호출이 되었다.

혹시 몰라서 it (그 매칭될 문자열) 앞뒤로 꺽쇠 괄호를 붙여서 출력을 해보았다.

>응답 BODY

<


으잉, 예상치 못하게 줄바꿈 문자가 있었다. 그래서 두 줄로 나왔다.


그래서 매칭이 되지 않던 것이구나.


하네스 코드에보면 traceResponse 메서드의 문자열을 만드는 반복문이 아래와 같이 구현이 되어 있다.

while (line != null) {
            inputStringBuilder.append(line);
            inputStringBuilder.append('\n');
            line = bufferedReader.readLine();
        }

코드를 보면 알 수 있지만 마지막에 줄바꿈 문자가 들어가게 되는 버그(?)가 숨어 있던 것이다.


이 코드를 아래처럼 바꾸고 나서야 원래의 테스트 코드는 녹색으로 바뀌었다.

while (line != null) {
if (inputStringBuilder.length() > 0) {
inputStringBuilder.append('\n');
}
inputStringBuilder.append(line);
line = bufferedReader.readLine();
}