How to avoid calls to the mock object method in the lines used for logging?

I wrote a test method in which there is a mocked object (say mockA). I can expect the mockA method to invoke the actual program logic. But part of my program also has a log that requires information about objects as a string. When creating a string message on objects, unnecessary methods are called. Thus, when performing a test, these method calls cause the test to fail. Here is 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 is an example of a 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 gives unexpected method calls to foo.getA (). If I create a good mock instead for Foo.class, it gives me a null pointer exception for foo.getB (). GetC () since B is an object inside foo. It is not possible to create nice mockery of all objects inside foo.

Is there a way to prevent such string operations that are used for logging? Or, alternatively, what can be done?

thanks

+6
source share
3 answers

There are two approaches. Firstly, you are mocking what is necessary for the correct interaction with Foo (as mentioned in another answer), or secondly, you extract all the complexity to separate the dependency.

In your example, complexity is creating messages. You can either:

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

Or the whole part of the magazine in a separate class:

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

This, of course, is introduced into the Example class as a constructor dependency. Then, in the test, you can easily mock it and forget about creating the entire log message (as it should be, given that this is not related to what the actual tested method does).

Now, whichever approach is chosen to a large extent, depends on the complexity of the objects involved (i.e., how difficult is it to actually create log messages? More complicated than setting up some extra calls?).

+2
source

How about mokito? check the possibility of returning deep stubs here

(especially Part why you shouldn't do this;)), then you can 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 could not find a similar technique in Easymock, and the answer here suggests there is nothing.


another idea:

if you can reorganize your code and use SLF4J as a logging structure, you can compose your log message inside the log call and do this only depending on the Loglevel level. Increase your log level to NONE inside the test, and foo.getA () ... will fail.

 public int method(Foo foo) { int a = foo.doSomething(); //required for program. logger.error("foo did something {}" , foo.getA() + foo.getB().getC()); return a; } 
+1
source

You can do something like

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

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

So foo.getB() will provide you with a mocking object. So there will be no NPE.

0
source

Source: https://habr.com/ru/post/956153/


All Articles