How to test logging in junit5?
Asked Answered
E

6

22

I am transitioning from junit4 to junit5 on a project and trying to figure out how to test logs. Previously, I used

@Rule
OutputCapture outputCapture = new OutputCapture();

and would then write an assertion using outputCapture.toString(), for example

assertThat(outputCapture.toString(),containsString("status=200"));

Since @Rule annotation hasn't been implemented yet in junit5, I can't use outputCapture. Any ideas what to do instead? Thanks!

Emelina answered 13/3, 2017 at 14:24 Comment(0)
S
20

There's an extension provided for the same and you can use it as below:

@ExtendWith(OutputCaptureExtension.class)
public class MyTestClass {

    @Test
    void myTestCase(CapturedOutput capturedOutput) {
        assertTrue(capturedOutput.getOut().contains("expected string"));
        assertTrue(capturedOutput.getErr().contains("expected string"));
    }
}
Stonecrop answered 12/4, 2021 at 9:0 Comment(2)
import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension;Solidary
@Solidary What about not spring(boot) projects?Fondea
S
7

We stumbled upon the same issue during our JUnit5 migration. After some research, I found a technical solution but it didn't seem like someone made a testing library out of it just yet. So that's what I did. It's published to Maven Central so you can use it right away:

https://github.com/netmikey/logunit

You can use it as follows:

public class MyModuleTest {

    @RegisterExtension
    LogCapturer logs = LogCapturer.create().captureForType(MyModule.class);

    @Test
    public void testLogging() {
        // ... do the testing ...

        // Run assertions on the logged messages
        logs.assertContains("Some message");
    }
}

(see the project's README for more examples)

Sirotek answered 15/3, 2019 at 10:7 Comment(1)
implemented your library, excellent addition to my project!. Nice work!Luge
T
3

The Migration Tip from the JUnit5 documentation clearly states that -

@Rule and @ClassRule no longer exist; superseded by @ExtendWith; see the following section for partial rule support.

For the purpose of using the existing @Rule support from JUnit 4, there is though a way suggested for method or class level annotations.

As in JUnit 4, Rule-annotated fields as well as methods are supported. By using these class-level extensions on a test class such Rule implementations in legacy codebases can be left unchanged including the JUnit 4 rule import statements.

This limited form of Rule support can be switched on by the class-level annotation org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport


A better option would still be redesigning your test suite to use the Extension Model from JUnit5 if you're using it.

Toil answered 13/3, 2017 at 17:35 Comment(0)
P
3

You can also easily test the log output written to System.out by quickly implementing a solution on your own as follows:

// Configure System.out to be written to a buffer from which we can read
PrintStream realSysOut = System.out;
BufferedOutputStream sysOutBuffer = new ByteArrayOutputStream();
System.setOut(new PrintStream(sysOutBuffer));
...
// Perform some action which logs something to System.out
System.out.println("Some random content written to System.out");
...
// Assert that a given string was written in the meantime to System.out
assertThat(new String(buffer.toByteArray()), containsString("random content"));
...
// Don't forget to bring back the real System.out at the end of the test
System.setOut(realSysOut);

In the case of checking log output written to System.err, you can equivalently implement it by replacing System.setOut(...) with System.setErr(...) and System.out with System.err in the example above.

Procrustean answered 21/10, 2020 at 6:54 Comment(0)
K
1

If what you want to do is to check if the class under test writes correct logs, another way is to configure a ListAppender (which is already provided by LogBack) and point your logger name to that appender in your logging configuration. The appender writes the logs to a java.util.List and then you can get the appender via the logging framework's API. Then you can check the contents of the list.

LogBack's default ListAppender is not thread-safe so you may want to write something similar with any synchronization if necessary.

Here is how we do it with SLF4j and logback:

import ch.qos.logback.classic.Logger
import ch.qos.logback.core.read.ListAppender
....    
Logger logger = (Logger) LoggerFactory.getLogger("some.logger")
ListAppender appender = (ListAppender) logger.getAppender("SOME_LIST")

In your logback.xml file, you configure the appender:

<appender name="SOME_LIST" class="ch.qos.logback.core.read.ListAppender">
    <encoder>
      <pattern>${LOG_PATTERN}</pattern>
    </encoder>
  </appender>

<logger name="some.logger" level="DEBUG">
    <appender-ref ref="SOME_LIST" />
  </logger>
Kataway answered 9/1 at 5:21 Comment(0)
K
1

If you are using Springboot, you can use CapturedOutput and check if what you logged appeared.

Your class:

@Slf4j
public class MyClass {
    public void myLogFunction() {
        log.info("Hello world !";
    }
}

Your test class:

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;

import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(OutputCaptureExtension.class)
class MyTestClass {

    @Test
    void MyLogTest (CapturedOutput output) {
    // my conditions
    when(...).thenReturn(...);
    
    // my tests having some logs
    myLogFunction();
    
    // checking my logs appeared in log output
    Assertions.assertTrue(output.getOut().contains("Hello world !"));
    }
}
Krohn answered 19/2 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.