Uploaded image for project: 'logback'
  1. logback
  2. LOGBACK-1615

Simplify Testing with ListAppender

    XMLWordPrintable

Details

    • Icon: Improvement Improvement
    • Resolution: Unresolved
    • Icon: Major Major
    • None
    • None
    • logback-core
    • None

    Description

      My understanding is the main usage of ListAppender is for writing tests that verify expected logs are produced.

      Currently, setting this up involves a lot of boilerplate:

      package com.mypackage;
      
      import static org.junit.jupiter.api.Assertions.assertTrue;
      
      import ch.qos.logback.classic.Logger;
      import ch.qos.logback.classic.spi.ILoggingEvent;
      import ch.qos.logback.core.read.ListAppender;
      import org.junit.jupiter.api.Test;
      import org.slf4j.LoggerFactory;
      
      
      public class TestMyClass {
          static ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
      
          static {
              listAppender.start();
              ((Logger) LoggerFactory.getLogger(MyClass.class)).addAppender(listAppender);
              ((Logger) LoggerFactory.getLogger(AnotherClass.class)).addAppender(listAppender);
          }
      
          @Test
          public void myTest() {
              MyClass.doThingThatLogs();
              int lastLogIndex = listAppender.list.size() - 1;
      
              // Note: When it fails, it unhelpfully says "False is not True".
              assertTrue(
                  listAppender
                      .list
                      .get(lastLogIndex)
                      .getFormattedMessage()
                      .contains("My expected log"));
          }
      }

      I believe this is a common use case for ListAppender, and the class should be improved to facilitate doing this kind of thing better and with a lot less code.

      Off the top of my head, the following improvements could be made:

      1. Give ListAppender a constructor that takes in a vararg of classes or Strings to list itself to, and have it start itself.
      2. Give ListAppender a size method that skips the need to go to the underlying list.
      3. Give ListAppender a get method that skips asking the underlying list, and also support negative indexes to easily facilitate getting logs from the end rather than the start.
      4. Give ListAppender an assertContains(int index, String... substrings) method that facilitates easily asserting that specific substrings showed up in the logs, and generates a good error message of what log was actually found vs which expected substrings were missing.

      With those proposals, the initial example test could be reduced down to just:

      package com.mypackage;
      
      import ch.qos.logback.classic.spi.ILoggingEvent;
      import ch.qos.logback.core.read.ListAppender;
      import org.junit.jupiter.api.Test;
      
      
      public class TestMyClass {
          static ListAppender<ILoggingEvent> listAppender = new ListAppender<>(MyClass.class, AnotherClass.class);
      
          @Test
          public void myTest() {
              MyClass.doThingThatLogs();
      
              // Note: If the log is missing, the failure message says what it actually found.
              listAppender.assertContains(-1, "My expected log");
          }
      
      

      I wasn't sure if people maybe thought this should go someplace different (maybe make a subclass of ListAppender?) or if it doesn't belong in Logback. I'm guessing the value:cost ratio is quite good here. Do others have suggestions to improve the proposed method signatures or have additional suggestions of what would be valuable here?

      Getting somewhat off-topic, it could utilize of try-with-resources to easily facilitate start/stop and making sure logs aren't leaked between tests.

      I'm happy to make a PR that adds the code in + unit tests to cover it, if maintainers think this is a good idea at all.

      Attachments

        Activity

          People

            logback-dev Logback dev list
            TaylorSMarks Taylor S Marks
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated: