How to Check For a Logged Message in a Spock Test
1. 소개
코드가 실행될 때 우리는 종종 관심 있는 이벤트를 기록합니다. 이러한 이벤트 중 일부는 충분히 중요하여 메시지가 기록되도록 확인하고 싶습니다.
이 튜토리얼에서는 Spock 테스트에서 우리 클래스가 특정 메시지를 기록했는지 확인하는 방법을 배울 것입니다. Logback의 ListAppender를 사용하여 나중에 검사할 수 있도록 메시지를 캡처합니다. Logback은 Spring의 기본 로깅 프레임워크입니다.
2. 설정
먼저, 정보를 기록하고 확인하고자 하는 경고 메시지를 기록하는 ClassWithLogger Java 클래스를 만들어 보겠습니다. Slf4j 주석을 사용하여 로거를 생성하고, 정보 수준에서 메시지를 기록하는 메서드를 추가해 보겠습니다:
@Slf4j
public class ClassWithLogger {
void logInfo() {
log.info("info message");
}
}
3. 테스트 프레임워크
이제 ClassWithLogger의 인스턴스를 주제로 하는 ClassWithLoggerTest Specification을 만들어 보겠습니다:
class ClassWithLoggerTest extends Specification {
@Subject
def subject = new ClassWithLogger()
}
다음 단계로, ClassWithLogger의 메시지를 수신할 Logger를 설정해야 합니다. 따라서, LoggerFactory에서 Logger를 가져오고 Logback의 기능을 사용하기 위해 Logback Logger 인스턴스로 형변환해 보겠습니다.
def logger = (Logger) LoggerFactory.getLogger(ClassWithLogger)
그 다음, 메시지를 수집하기 위해 ListAppender를 생성하고, 앱펜더를 Logger에 추가하겠습니다. 수집된 메시지가 각 테스트마다 리셋되도록 하기 위해 setup 메서드를 사용하겠습니다:
def setup() {
listAppender = new ListAppender<>()
listAppender.start()
logger.addAppender(listAppender)
}
ListAppender를 설정했으므로, 로그 메시지를 수신하고 수집하며 테스트에서 검증할 준비를 마쳤습니다.
4. 간단한 메시지 확인하기
이제 우리의 첫 번째 테스트를 작성하여 메시지를 기록하고 확인해 보겠습니다. 그래서, ClassWithLogger의 logInfo 메서드를 호출하여 INFO 메시지를 기록하고, 예상되는 텍스트와 로그 수준이 포함된 ListAppender의 메시지 List에 존재하는지 확인하겠습니다:
def "when our subject logs an info message then we validate an info message was logged"() {
when: "we invoke a method that logs an info message"
subject.logInfo()
then: "we get our expected message"
with(listAppender.list[0]) {
getMessage() == expectedText
getLevel() == Level.INFO
}
}
이 경우, 우리의 메시지가 기록된 첫 번째이자 유일한 메시지라는 것을 알 수 있으므로 ListAppender의 캡처된 메시지 목록에서 인덱스 0에서 안전하게 확인할 수 있습니다.
우리가 찾고 있는 로그 이벤트가 반드시 첫 번째 메시지가 아닐 경우, 좀 더 강력한 접근 방식이 필요합니다. 이 경우 메시지 목록을 스트리밍하고 우리의 메시지에 대해 필터링해 보겠습니다:
then: "we get our expected message"
def ourMessage = listAppender.list.stream()
.filter(logEvent -> logEvent.getMessage().contains(expectedText))
.findAny()
ourMessage.isPresent()
and: "the details match"
with(ourMessage.get()) {
getMessage() == expectedText
getLevel() == Level.INFO
}
5. 포맷된 메시지 확인하기
LoggingEvent에서 getMessage를 호출하면 포맷된 문자열이 아닌 템플릿이 반환됩니다. 간단한 메시지의 경우 이것이 전부지만, 로그 메시지에 매개 변수를 추가할 경우 실제로 기록된 메시지를 확인하기 위해서는 getFormattedMessage를 사용해야 합니다.
따라서, 템플릿 String과 매개 변수를 사용하여 메시지를 기록하는 logInfoWithParameter 메서드를 ClassWithLogger에 추가해 보겠습니다:
void logInfoWithParameter(String extraData) {
log.info("info message: {}", extraData);
}
이제, 우리의 새로운 메서드를 호출하기 위한 테스트를 작성해 보겠습니다:
def "when our subject logs an info message from a template then we validate an info message was logged with a parameter"() {
when: "we invoke a method that logs info messages"
subject.logInfoWithParameter("parameter")
}
마지막으로, 첫 번째로 기록된 메시지를 확인하여 LoggingEvent의 getArgumentArray에서 첫 번째 항목이 우리의 매개변수 값과 일치하는지 확인하고, getFormattedMessage를 사용하여 전체 메시지를 검증하겠습니다:
then: 'the details match for the first message in the list'
with (listAppender.list[0]) {
getMessage() == 'info message: {}'
getArgumentArray()[0] == 'parameter'
getFormattedMessage() == 'info message: parameter'
getLevel() == Level.INFO
}
참고: getMessage는 템플릿 String을 반환하는 반면, getFormattedMessage는 실제로 기록된 메시지를 반환합니다.
6. 요약
이 기사에서는 Logback의 ListAppender를 사용하여 로그 메시지를 수집하고 LoggingEvent를 사용하여 간단한 메시지를 확인하는 방법을 배웠습니다. 또한 템플릿 문자열과 매개변수를 사용하여 기록된 메시지를 검증하는 방법도 배웠습니다.
이 튜토리얼의 소스는 GitHub에서 확인할 수 있습니다.