0

I have a method I am trying to test, say methodToTest(). Inside this method is a call to what I believe is a Logger variable, like:

logger.debug("this is a logger debug example");

The problem is that the logger variable is declared outside the class (it appears to be inherited), to which I do not have the source for.

When I create an instance of the test class, and then call the methodToTest() method, I get a NullPointerException at the logger.debug statement.

How can I mock the logger entirely? It has no use during testing istself.

when(logger.debug("")).doNothing();

Gives me an error, even when I create a Logger variable within the test class.

I'm thinking I should maybe use spying, but I'm not sure how, and then not sure what to do with my current when().thenReturn(); statements.

Per @kamil's suggestion, the

f.set( LM2DImportMultiTaskWorker.class, null );

code threw the following error:

java.lang.IllegalArgumentException: Can not set org.apache.log4j.Logger field com.navteq.cf.foundation.process.OperationSkeleton.logger to java.lang.Class
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(Unknown Source)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(Unknown Source)
    at java.lang.reflect.Field.set(Unknown Source)
    at com.navteq.rdf.base.task.LM2DImportMultiTaskWorkerTest.testGetCountFromDB(LM2DImportMultiTaskWorkerTest.java:101)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
    at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
DanGordon
  • 671
  • 3
  • 8
  • 26
  • Apparently, you need to do little more work (hack!~1) to access final field. I'll update my answer once more. Moreover, I made mistake and adviced you to use wrong `field.set` method. – kamil Jun 30 '14 at 17:34

2 Answers2

1

You can use reflections for this like that:

Field f = Yourclass.class.getDeclaredField("logger");
f.setAccessible(true);
f.set(Yourclass.class, yourMock );

When you need to search for your field up to super class, you could make some look-up loop:

Class clazz = Yourclass.class;

while ( clazz != null ) {
    Field f = null;
        try {
            f = clazz.getDeclaredField( "logger" );
        } catch ( NoSuchFieldException e ) {
            clazz = clazz.getSuperclass();
        }
    if ( f != null ) {
        f.setAccessible( true );

        //here we must hack final modifier for field
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);

        //here pass your object which should contain logger field
        f.set( yourObject,  mock(mockSomeClass) );
        break;
    }
}
kamil
  • 3,482
  • 1
  • 40
  • 64
  • But doesn't this assume that the `Logger logger` variable is declared within my test class? I mention above that it is not. – DanGordon Jun 30 '14 at 16:29
  • YOu can checkout superclass for your field, I'll update answer – kamil Jun 30 '14 at 16:31
  • That didn't work. It is not the immediate superclass that declares the `logger` variable, and I'm not sure the relation to that class at all, as I have no source code for the class that the Logger is declared in. – DanGordon Jun 30 '14 at 16:50
  • Actually, eliminated the `if (f != null)` lines of code allowed my test to pass. I'm not sure I understand this. – DanGordon Jun 30 '14 at 16:56
0

Assuming you can change the code, the easiest/cleanest way in my opinion would be to inject a Logging interface to the class you're testing. Then either mock it or make a Fake logging implementation with a no-op for the .debug method.

Pacane
  • 20,273
  • 18
  • 60
  • 97