6

I have written a test method in which there is a mocked object(say mockA). I am able to expect mockA's method calls for the actual program logic. But, part of my program also has logging which require objects' information in the form of string. While creating the string message, there are some unnecessary methods being called on the objects. So, while running the test, these method calls are causing the test to fail. Here's an example.

public class Example {
    public int method(Foo foo) {
       int a = foo.doSomething(); //required for program.
       String logMessage = "foo did something." + foo.getA() + foo.getB().getC();
       logger.log(logFile, logMessage);
       return a;
    }
}

Here's the example test method.

@Test
public void testMethod() {
   int something = 0;
   Foo mockFoo = EasyMock.createMock(Foo.class);
   expect(mockFoo.doSomething()).andReturn(something);
   EasyMock.replay(mockFoo);
   assertEquals(new Example().method(mockFoo), something);
   EasyMock.verify(mockFoo);
}

This is giving unexpected method calls for foo.getA(). If I create a nice mock instead for Foo.class, it gives me null pointer exception for foo.getB().getC() since B is an object inside foo. It's not possible for me to create nice mocks of all objects inside foo.

Is there anyway to prevent such string operations which are used for logging? Or alternatively, what could be done?

Thanks

3 Answers3

2

There are two approaches. First, you mock everything that's needed for proper interaction with Foo (as mentioned in other answer) or second, you extract whatever the complexity is to separate dependency.

In your example, the complexity is message creation. You could either have:

class FooLogMessageFactory {
    public string createLogSomethingMessage(Foo foo) {
        return "foo did something." + foo.getA() + foo.getB().getC();
    }
}

Or entire logging part in separate class:

class FooDedicatedLogger {
    public void logOnSomething(Foo foo) {
        String message = "foo did something." + foo.getA() + foo.getB().getC();
        logger.log(logFile, message);
    }
}

This is of course injected to your Example class as constructor dependency. Then in test you can mock it easily and forget about log message creation entirely (as it should be, given it's irrelevant to what actual tested method does).

Now, whichever approach to choose largely depends on the complexity of objects involved (i.e. How difficult is it to actually build log message? More difficult than setting up few extra mock calls?).

k.m
  • 30,794
  • 10
  • 62
  • 86
1

How about Mockito? check out the possibility to return deep Stubs here

(especially the Part why you shouldn't do it ;) ), then you could write your test as:

@Test
public void testMethod() {
   int something = 0;
   Foo mockFoo = mock(Foo.class, RETURNS_DEEP_STUBS);
   when(mockFoo.doSomething()).thenReturn(something);
   assertEquals(new Example().method(mockFoo), something);
   verify(mockFoo);
}

I couldn't find any similar technique in Easymock, and the answer here suggests there isn't anything.


another Idea:

if you can refactor your code and use SLF4J as a logging framework, you can compose your Logging message inside the logger call and only do that depending on the Loglevel. Ramp up your Log level to NONE inside the test and the foo.getA() ... will not be executed.

public int method(Foo foo) {
   int a = foo.doSomething(); //required for program.
   logger.error("foo did something {}" , foo.getA() + foo.getB().getC());
   return a;
}
Community
  • 1
  • 1
Christian Uhl
  • 362
  • 1
  • 9
  • Thanks for the ideas, Uhl. I've been using EasyMock, so moving on to Mockito would be slightly uncomfortable. The second idea involves log message creation inside the method call which might not work if I want to use those strings elsewhere. The answer given by jimmy_keen helps. Thanks. – Vincent Vega Oct 18 '13 at 06:04
0

You can do something like

B b = EasyMock.createMock(B.class);

expect(mockFoo.getB()).andReturn(b);

So the foo.getB() will give you mocked object. So there wont be a NPE.

Sajith Silva
  • 823
  • 1
  • 13
  • 24
  • Yeah, I can do it but as I mentioned in the question itself, the object may contain, not just one, but many other objects in it. So, if the string is something like foo.getB().getC() + foo.getX().getY().getZ() and so on.. it is not a feasible solution. – Vincent Vega Oct 17 '13 at 10:09
  • 1
    if you use log4j or similar you can check if isXXXXEnabled() and if so you can do the string operation you are doing and actual logging. So in your test have a different log level so that part of the code is never executed. – Sajith Silva Oct 17 '13 at 10:17
  • Thank you for you contribution. :-) – Vincent Vega Oct 18 '13 at 06:05