flogger icon indicating copy to clipboard operation
flogger copied to clipboard

Add samples to show using Flogger in JUnit

Open anantdamle opened this issue 4 years ago • 3 comments

As part of unit testing, it becomes important to test the Logged messages in case of exceptions or other such scenarios.

Is this the recommended way to test logging behaviour in tests?

 @Test
  public void read_invalidPath_logsError() {
    Handler mockHandler = Mockito.mock(Handler.class);
    ArgumentCaptor<LogRecord> logRecordCaptor = ArgumentCaptor.forClass(LogRecord.class);
    //set mock handler with Argument Captor
    Logger.getLogger(JsonMessageParser.class.getName()).addHandler(mockHandler);

    JsonMessageParser.of(TEST_JSON).read("$.code2");

    verify(mockHandler).publish(logRecordCaptor.capture());

    assertThat(logRecordCaptor.getValue().getMessage()).contains("error reading [$.code2]");
  }

anantdamle avatar Jun 19 '20 14:06 anantdamle

If you know the backend you're using, you can just test using the backend's idiomatic approach. E.g. for JUL, just install a test log handler to capture the records and then assert on them.

I wouldn't think you'd need to do any mocking here though.

hagbard avatar Jun 19 '20 19:06 hagbard

I was using that technique and It works when the loggers are not rate-limited

The LoggerBackend does not get the logData in the following case, if there are more than one failing Test-cases. The following unit-test becomes flaky, it sometimes works and fails other times.

e.g. If the call site is

private static final logger = FluentLogger.forEnclosingClass();
...
public String processedItem() {
    try{
        return processItem();
    } catch (ProcessingException exception) {
        logger.atInfo().atMostEvery(1, TimeUnit.MINUTE).withCause(exception).log("item %s was not processed");
       return null;
    }
}

Unit Tests as follows The assertion on logger does not work consistently.

@Test
public void processedItem_failureCase_null() {
  assertThat(processedItem()).isNull();
}

@Test
public void processedItem_failureCase2_null() {
    Handler mockHandler = Mockito.mock(Handler.class);
    ArgumentCaptor<LogRecord> logRecordCaptor = ArgumentCaptor.forClass(LogRecord.class);
    //set mock handler with Argument Captor
    Logger.getLogger(JsonMessageParser.class.getName()).addHandler(mockHandler);

    // Method under test
    processedItem()    

    verify(mockHandler).publish(logRecordCaptor.capture());
    assertThat(logRecordCaptor.getValue().getMessage()).contains("item %s was not processed");
}

anantdamle avatar Jun 19 '20 21:06 anantdamle

Ahh yes, the right way to do that is using the GrpcScopedLoggingContext which sadly I don't think is open-sourced yet.

Basically if your Platform instance returns true for "shouldForceLogging" then everything will work, but the "nice" mechanism for that is using this GrpcScopedLoggingContext thing and that's not pushed yet (the code is pretty much done though, so I will see about getting it out soon).

Once that's there it's easy to make a JUnit rule which install a context for the duration of a test which forces all logging to happen.

Sorry to not have a better answer for you :(

hagbard avatar Jun 19 '20 22:06 hagbard