JUnitを使用したログメッセージのアサート
1. 序章
このチュートリアルでは、JUnitテストで生成されたログをカバーする方法を見ていきます。
slf4j-apiとログバック実装を使用して、ログアサーションに使用できるカスタムアペンダーを作成します。
2. Mavenの依存関係
始める前に、logback依存関係を追加しましょう。 slf4j-api をネイティブに実装しているため、Mavenの推移性によって自動的にダウンロードされ、プロジェクトに注入されます。
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>.
<version>1.2.6</version>
</dependency>
AssertJ はテスト時に非常に便利な関数を提供するので、その依存関係もプロジェクトに追加しましょう。
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.15.0</version>
<scope>test</scope>
</dependency>
3. 基本的なビジネス機能
それでは、テストの基礎となるログを生成するオブジェクトを作成しましょう。
BusinessWorker オブジェクトは、1つのメソッドのみを公開します。 このメソッドは、ログレベルごとに同じ内容のログを生成します。 この方法は現実の世界ではそれほど有用ではありませんが、テスト目的には役立ちます。
public class BusinessWorker {
private static Logger LOGGER = LoggerFactory.getLogger(BusinessWorker.class);
public void generateLogs(String msg) {
LOGGER.trace(msg);
LOGGER.debug(msg);
LOGGER.info(msg);
LOGGER.warn(msg);
LOGGER.error(msg);
}
}
4. ログのテスト
ログを生成したいので、 src / test /resourcesフォルダーにlogback.xmlファイルを作成しましょう。 できるだけシンプルに保ち、すべてのログをCONSOLEアペンダーにリダイレクトしましょう。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
</Pattern>
</layout>
</appender>
<root level="error">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
4.1. MemoryAppender
それでは、ログをメモリに保持するカスタムアペンダーを作成しましょう。 良い ListAppenderを拡張します
public class MemoryAppender extends ListAppender<ILoggingEvent> {
public void reset() {
this.list.clear();
}
public boolean contains(String string, Level level) {
return this.list.stream()
.anyMatch(event -> event.toString().contains(string)
&& event.getLevel().equals(level));
}
public int countEventsForLogger(String loggerName) {
return (int) this.list.stream()
.filter(event -> event.getLoggerName().contains(loggerName))
.count();
}
public List<ILoggingEvent> search(String string) {
return this.list.stream()
.filter(event -> event.toString().contains(string))
.collect(Collectors.toList());
}
public List<ILoggingEvent> search(String string, Level level) {
return this.list.stream()
.filter(event -> event.toString().contains(string)
&& event.getLevel().equals(level))
.collect(Collectors.toList());
}
public int getSize() {
return this.list.size();
}
public List<ILoggingEvent> getLoggedEvents() {
return Collections.unmodifiableList(this.list);
}
}
MemoryAppender クラスは、ロギングシステムによって自動的に入力されるListを処理します。
幅広いテスト目的をカバーするために、さまざまな方法を公開しています。
- reset() –リストをクリアします
- contains(msg、level) –リストに指定されたコンテンツと重大度レベルに一致する ILoggingEvent が含まれている場合にのみ、trueを返します
- countEventForLoggers(loggerName) –名前付きロガーによって生成されたILoggingEventの数を返します
- search(msg) –特定のコンテンツに一致するILoggingEventのListを返します
- search(msg、level) –指定されたコンテンツと重大度レベルに一致するILoggingEventのListを返します
- getSize() – ILoggingEventの数を返します
- getLoggedEvents() – ILoggingEvent要素の変更不可能なビューを返します
4.2. 単体テスト
次に、ビジネスワーカー用のJUnitテストを作成しましょう。
MemoryAppender をフィールドとして宣言し、プログラムでログシステムに挿入します。 次に、アペンダーを起動します。
テストでは、レベルをDEBUGに設定します。
@Before
public void setup() {
Logger logger = (Logger) LoggerFactory.getLogger(LOGGER_NAME);
memoryAppender = new MemoryAppender();
memoryAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
logger.setLevel(Level.DEBUG);
logger.addAppender(memoryAppender);
memoryAppender.start();
}
これで、 BusinessWorker クラスをインスタンス化し、generateLogsメソッドを呼び出す簡単なテストを作成できます。 次に、生成されたログに対してアサーションを作成できます。
@Test
public void test() {
BusinessWorker worker = new BusinessWorker();
worker.generateLogs(MSG);
assertThat(memoryAppender.countEventsForLogger(LOGGER_NAME)).isEqualTo(4);
assertThat(memoryAppender.search(MSG, Level.INFO).size()).isEqualTo(1);
assertThat(memoryAppender.contains(MSG, Level.TRACE)).isFalse();
}
このテストでは、MemoryAppenderの3つの機能を使用します。
- 4つのログが生成されました—重大度ごとに1つのエントリが存在し、トレースレベルがフィルタリングされている必要があります
- レベルの重大度がINFOのコンテンツmessageのログエントリは1つだけです。
- コンテンツメッセージおよび重大度TRACEのログエントリはありません
大量のログを生成するときに同じテストクラス内でこのクラスの同じインスタンスを使用することを計画している場合、メモリ使用量は増加します。 各テストの前にMemoryAppender.clear()メソッドを呼び出して、メモリを解放し、OutOfMemoryExceptionを回避できます。
この例では、保持されるログの範囲をLOGGER_NAMEパッケージに縮小しました。これを「com.baeldung.junit.log」と定義しました。 LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)を使用してすべてのログを保持できる可能性がありますが、大量のメモリを消費する可能性があるため、可能な限りこれを回避する必要があります。
5. 結論
このチュートリアルでは、単体テストでログ生成をカバーする方法を示しました。
いつものように、コードはGitHubのにあります。