2

I am trying to test something like this:

try {
     logger.info("message");
     //do something
} catch(Exception e) {
     logger.error(errorMessage);
}

I know that it's not a good practice to catch an Exception, but there is some legacy code and there is no time for refactoring.

So, I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time). Please notice that I can`t use:

        final Logger logger = LogManager.getLogger(AnaliticsService.class);
        final Appender mockAppender = mock(Appender.class);
        logger.addAppender(mockAppender);
        final ArgumentCaptor<LoggingEvent> captor = ArgumentCaptor.forClass(LoggingEvent.class);
        Log4jConfigHelper.getInstance().bufferConfiguration();
        verify(mockAppender, times(x)).doAppend(captor.capture());

because I don`t know how many messages are logged when UT is running.

lucian.marcuta
  • 1,250
  • 1
  • 16
  • 29

3 Answers3

0

You should try to make a Mock for LoggerFactory.

First annotate your TestClass with:

@RunWith(PowerMockRunner.class)
@PrepareForTest({YourController.class, LoggerFactory.class})

Then make a test, which calls needed method and veryfies errors:

@Test
public void testErrorLogging() throws Exception {
    mockStatic(LoggerFactory.class);

    Logger logger = mock(Logger.class);
    when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);

    YourController controller = new YourController();
    controller.someMethod();

    verify(logger).error(anyString());
}
Stanislav
  • 27,441
  • 9
  • 87
  • 82
0

Log messages are part of the user interface of your code. Code that does computations should not make assumptions about the manner in which log messages are made available to the user, the text and language of the log messages, or even whether messages are communicated as text (rather than, say, a graphical means). So computational code should delegate to an associated logger class (in the UI/presentation layer) that hides all those details.

If the computational code only requires that the associated logger conforms to an interface, and uses dependency injection for being associated with a logger class, it is easy to mock the logger to examine whether the computational code has requested logging.

So if the code to be tested is like this::

 public class MyService
 {
    private final MyServiceLogger logger;

    MyService(MyServiceLogger logger)
    {
       this.logger = Objects.requireNonNull(logger);
    }

    public void processFile(Path path) {
       ...
       try{
          ...
       } catch (EOFException e) {
          logger.logUnexpectedEOF(path);
       }
    }
 }

 public interface MyServiceLogger
 {
    public logUnexpectedEOF(Path path);
 }

 public class MyServiceTextLogger implements MyServiceLogger
 {
    private final Logger textLogger = LogManager.getLogger(MyService.class);;

    @Override
    public logUnexpectedEOF(Path path) {
       textLogger.error("unexpected EOF for file {}",path);
    }
 }

You can test it like this:

 public class MyServiceTest
 {
    private static class MockMyServiceLogger implements MyServiceLogger
    {
       private Path path;
       private int nCalls_logUnexpectedEOF;

       @Override
       public logUnexpectedEOF(Path path) {
          ++nCalls_logUnexpectedEOF;
          this.path = path;
       }

       void assertCalled_logUnexpectedEOF(int nCalls, Path path) {
          assertEquals("Called logUnexpectedEOF, nCalls", nCalls, nCalls_logUnexpectedEOF);
          assertEquals("Called logUnexpectedEOF, path", path, this.path);
       }
    }

    @Test
    public void processFile_unexpectedEOF() {
       Path testPath = ...
       ...
       MockMyServiceLogger mockLogger = new MockMyServiceLogger();
       MyService service = new MyService(mockLogger);

       service.processFile(testPath);

       mockLogger.assertCalled_logUnexpectedEOF(1, testPath);
    }

    @Test
    public void processFile_OK() {
       Path testPath = ...
       ...
       MockMyServiceLogger mockLogger = new MockMyServiceLogger();
       MyService service = new MyService(mockLogger);

       service.processFile(testPath);

       mockLogger.assertCalled_logUnexpectedEOF(0, null);
    }
 }
Raedwald
  • 46,613
  • 43
  • 151
  • 237
-1

I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time).

You don't need to check for an exception this way. A test which throws an Exception fails.

} catch(Exception e) {
     logger.error(errorMessage, e);
     throw e; // report the error to the test
}

Note: when to throw an error to the testing framework it will log/print it so I suspect you don't need to be catching it in the first place.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130