Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid mock object's method calls in strings used for logging?

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

like image 954
Vincent Vega Avatar asked Oct 17 '13 09:10

Vincent Vega


People also ask

Can you call method on mocked object?

Mockito allows us to partially mock an object. This means that we can create a mock object and still be able to call a real method on it.

Which method call can be used to setup a mock object method?

We can use Mockito class mock() method to create a mock object of a given class or interface. This is the simplest way to mock an object. We are using JUnit 5 to write test cases in conjunction with Mockito to mock objects.

How do you know if a mock method is called?

Mockito verify() method can be used to test number of method invocations too. We can test exact number of times, at least once, at least, at most number of invocation times for a mocked method. We can use verifyNoMoreInteractions() after all the verify() method calls to make sure everything is verified.

How can specify the mock Behaviour of an object?

When you create a mock, you create an associated behavior object that controls mock behavior. Use this object to define mock method and property behavior (stub). For more information on creating a mock, see Create Mock Object.


1 Answers

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?).

like image 68
k.m Avatar answered Oct 24 '22 09:10

k.m