[JAVA] Test the behavior of the log output logger when using an external logging API such as SLF4J

background

I want to check the log of the log output at the time of testing when using an external logging API such as SLF4J.

Purpose

Mock the log so that you can control the output of the log with test code

For those who want to take a quick look at the code, "[Sample Code](https://qiita.com/yusha3/items/c9576bbe8b6cc5b27897#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%" You can find it in the chapter "AB% E3% 82% B3% E3% 83% BC% E3% 83% 89)".

approach

Procedure flow

  1. Definition of ArgmentCaptor
  2. Define the Logger of the target class and insert the mock appender.
  3. Catch the call specified by the logger mock instance.

1. Definition of ArgmentCaptor

  @Captor
  private ArgumentCaptor<LoggingEvent> captorLoggingEvent;

ArgmentCaptor is used when you want to verify that a stubbed method in a mockito class has been called properly, or that the appropriate arguments have been passed to that method. This time, we will capture the argument of LoggingEvent which is the type of log output.

2. Define the Logger of the target class and insert the mock appender.

	  @Mock
	  private Appender mockAppender;

      final Logger logger = (Logger) LoggerFactory.getLogger([Class name to be tested].class);
      logger.addAppender(mockAppender);

Define your own logger variable without using @ Slf4j and add a mocked Appender to logger. Appender is the interface used to set the log output destination. Appender can be set to Logger with the addAppender () method. Here, you can mock the log output destination of logger by preparing a mock Appender and ʻaddAppender ()` it.

3. Catch the call specified by the logger mock instance

     verify(mockAppender).doAppend(captorLoggingEvent.capture());
     assertEquals("INFO", captorLoggingEvent.getValue().getLevel().toString());

doAppend has an event object as an argument. The role that the append Appender logs

Only the last log can be caught by the above method. Assign to a list to catch all logs.

//Extract only application logs
      List<LoggingEvent> events = captorLoggingEvent.getAllValues().stream()														 
                                  .collect(Collectors.toList());

Sample code

Main class

package com.zozo.tech.search.personalize.api.logic;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class Sample {

  public void doLogging() {
    log.info("sample");
    log.info("sample access_log.");
    log.info("sample access_log 2.");
  }
}

Test class

package com.zozo.tech.search.personalize.api.logic;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.verify;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class SampleTest {

  @Captor
  private ArgumentCaptor<LoggingEvent> captorLoggingEvent;

  @Mock
  private Appender mockAppender;

  @InjectMocks
  @Autowired
  Sample sample;

  @BeforeEach
  void setUp() {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  void test() throws Exception {
    Logger logger = (Logger) LoggerFactory.getLogger(Sample.class);
    logger.addAppender(mockAppender);

    sample.doLogging();

    verify(mockAppender).doAppend(captorLoggingEvent.capture());
    //Check if INFO level log is output
    assertEquals("INFO", captorLoggingEvent.getValue().getLevel().toString());

    //Extract only application logs
    List<LoggingEvent> events = captorLoggingEvent.getAllValues().stream()
        .filter(e -> e.getMessage().contains("access_log"))
        .collect(Collectors.toList());

    assertEquals(2, events.size());
  }
}

Reference material

Recommended Posts

Test the behavior of the log output logger when using an external logging API such as SLF4J
[Java] [Spring] Test the behavior of the logger
Precautions when using querySelector () (such as some ideas when specifying the name attribute)
When the form such as Select of Materialize does not work properly
Test the integrity of the aggregation using ArchUnit ②
Test the integrity of the aggregation using ArchUnit ③
Inspect the contents of log output during Java unit test (no mock used)
How to write an RSpec controller test
How to use "sign_in" in integration test (RSpec)
Introduction to RSpec 1. Test, RSpec
Use webmock with Rspec
How to use Chain API
Display weather forecast using OpenWeatherMap, an external API in Ruby
[Rails] Test code using Rspec
Test the behavior of the log output logger when using an external logging API such as SLF4J
[Ruby] Use an external API that returns JSON in HTTP request
Image upload using CarrierWave ~ Rspec test ~
How to insert an external library
[RSpec] How to write test code
[RSpec] Unit test (using gem: factory_bot)
[For Rails beginners] Summary of how to use RSpec (get an overview)
Java Artery-Easy to use unit test library
[Docker] Building an environment to use Hugo
How to play MIDI files using the Java Sound API (specify the MIDI device to use)