1

I am trying to do a full junit testing of my application and I am stuck when It comes to test the logger messages.

try {
    fillParameters(args);
} catch (ArgumentException e) {
    logger.error(e.getMessage(), e);
    return;
}

Here is the code which triggers the exception :

if (args.length > 3) {
    throw new ArgumentException("Wrong use of the command -> too many args"); 
}

One test :

@Test
public void testFillParametersWithTooManyArguments() {
    String[] args = { "...", "...", "...", "..." };
    Throwable e = null;
    try {
        testInstance.fillParameters(args);
    } catch (Throwable ex) {
        e = ex;
    }
    assertTrue(e instanceof ArgumentException); //this test is working as expected
}

when I take a look at the code coverage, the logger.error(e.getMessage(), e); part is not covered, how am I supposed to cover it? I assume I must mock the logger?

rilent
  • 659
  • 1
  • 6
  • 22
  • 1
    But if you are catching the exception, then logging and returning, your test won't ever catch the exception. – Andy Turner Oct 09 '18 at 15:19
  • catching and not rethrowing prevent the caller to know that an error has occured. You can log, but then rethrow the original error. – spi Oct 09 '18 at 15:20
  • @spi no, don't. Handle the error (e.g. by logging), or don't catch it and just let the caller handle it. If you do both, you risk doing something like logging in both places, which makes it look like there are two exceptions. – Andy Turner Oct 09 '18 at 15:24
  • this code is located in a main method, I just want the process to stop if this exception is triggered and log my exception in the console. Is something wrong with that? I would like to test if my logger.error() is called and log the correct message. – rilent Oct 09 '18 at 15:27
  • @AndyTurner in general yes, but there are special cases: if the caller is the main method, then your log statement can be usefull. It will benefit from your global logger configuration (rolling files, smtp appenders or whatever) instead of just having your shell behavior (output to System.err) – spi Oct 09 '18 at 15:28
  • @rilent yes, because the "java" command return code that launched your app will be 0 in this case (indicating "no error"), thus ultimately, the caller (the shell, bash, or batch) will not see any error – spi Oct 09 '18 at 15:28
  • let's say I don't kill the app by removing the return line (will kill it afterwards), how to test the logger? – rilent Oct 09 '18 at 15:32
  • @rilent it depends on the logging framework used. some can have a mechanism to build "mocks" logger natively. If not, you can't easily - it will require you to inject the mock to your class under test but that doesn't seem worth the effort. – spi Oct 09 '18 at 15:34
  • 1
    I am using log4j, I tried to mock the appender part but I couldn't succeed. edit : nvm I think the best way here is to throw my exception and test if the exception is being thrown – rilent Oct 09 '18 at 15:35
  • @rilent check this https://stackoverflow.com/q/42508323/6407858. Might help you. – Yug Singh Oct 09 '18 at 17:23

1 Answers1

0

Short Answer
Test the code that you actually want to test.

Some Info
The code in your first code block is in no way tested by the code in your example unit test. I assume that because it looks like Java code and the question is tagged as a Java question that the code in the first code block is actually in a method somewhere. You must unit that that method to get test coverage in the exception catch block in that method.

For Example:

public void IHateToTellPeopleMyMethodName(final String[] args)
{
    try
    {
        fillParameters(args);
    }
    catch (ArgumentException e)
    {
        logger.error(e.getMessage(), e);
        return;
    }
}

In order to get test coverage of the catch block in the IHateToTellPeopleMyMethodName method, you must test the IHateToTellPeopleMyMethodName method in your unit test.

This unit test method does nothing to test the IHateToTellPeopleMyMethodName method because it does not call the IHateToTellPeopleMyMethodName method.

@Test
public void testThatInNoWayTestsTheIHateToTellPeopleMyMethodNameMethod()
{
    String[] args = { "...", "...", "...", "..." };

    try
        {
        testInstance.fillParameters(args);
                fail("expected exception not thrown");
    }
        catch (Throwable ex)
        {
            assertTrue(e instanceof ArgumentException);
    }
}

Unlike the unit test code above, this unit test covers the IHateToTellPeopleMyMethodName method.

@Test
public void testTheIHateToTellPeopleMyMethodNameMethod()
{
    String[] args = { "...", "...", "...", "..." };

    testInstance.IHateToTellPeopleMyMethodName(args);

          verify(mockLogger).error(
                eq(EXPECTED_MESSAGE_TEXT),
                    any(ArgumentException.class));
}

Edit Note
My bad, any() needs a class object as a parameter, not a class name.

DwB
  • 37,124
  • 11
  • 56
  • 82