1

I want to mock a method that takes a function as input. And the function I want to test uses multiple functions with different return types. Is there a way to mock these kinds of functions.

These are my classes

public class Visitor {

    public <T> Function<String, T> visit(Function<Integer,T> transformer){
        return (input) -> {
            //Actual one has some complex code including I/O operations
            //To understand easily, added following lines
            int length = input.length();
            return transformer.apply(length);
        };
    }
}
public class Demo {
    private Visitor visitor;

    public Demo(Visitor visitor) {
        this.visitor = visitor;
    }

    public String demoFunction(){
        String input = "some input";
        String strOutput = visitor.visit(length -> {
          // Actual  actual code has multiple lines.
          // Added this line to understand functionality easily
         return Integer.toString(length);
         }).apply(input);
        Boolean boolOutput = visitor.visit(length -> length%2==0).apply(input);
        // other operations
        return strOutput+","+boolOutput;

    }
}

After referring this implemented this test case

public class DemoTest {

    @Test
    public void demoFunctionTest() {
        Visitor visitor = Mockito.mock(Visitor.class);
        Mockito.when(visitor.visit(Matchers.<Function<Integer, String>>any())).thenReturn(s -> "1");
        Mockito.when(visitor.visit(Matchers.<Function<Integer, Boolean>>any())).thenReturn(s -> true);

        Demo demo = new Demo(visitor);
        demo.demoFunction();
    }
}

But its failing when running test. This is the exception I got.

java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.String

at com.raghumolabanti.demo.Demo.demoFunction(Demo.java:15)
at com.raghumolabanti.demo.DemoTest.demoFunction(DemoTest.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)

Update: Actually code I have is complex and includes I/O in some places. It would be hard to include original code. To give simple working code, I created these example classes.

Raghu Molabanti
  • 317
  • 5
  • 16

2 Answers2

1

In this why do we want to mock Function when there is no necessary so. Mock is needed when business logics are included or any network calls made (Rest Template) etc. In this case you can just make the code execute and have a reference and then check its value.

Few things noticed in your test case.

  • This method does not take any input/ no return values. Means it's an helper method. in such cases can mock the place where you call this method instead mocking inside this function
  • You can make this fucntion takes an input parameter and return list of values u need to validate and then use assertMatchers to verify your result.

On lighter note, Mockito is powerful tool and if we mock every object and lines then there is no proper use of writing test cases because everything is mocked and what to check!

  • Updated code with comments. Actually code I have is complex and includes i/o in some places. It would be hard to include original code. To give simple working I created those example classes – Raghu Molabanti Apr 12 '20 at 03:44
0

Instances of the function parameter in Visitor class are easy to construct and may not be worth mocking.

The current testing code:

Mockito.when(visitor.visit(Matchers.<Function<Integer, String>>any())).thenReturn(s -> "1");
visitor.visit(length -> Integer.toString(length)).apply(input);

can just be replaced with:

visitor.visit(length -> 1).apply(input);

Then it would be a matter of making Demo expose the result of applying the visitor in order to test it.

This would be much more robust then trying to account for every type T of Function<Integer, T> that can be passed in and then manually stubbing a behavior.

Reference: Don't Overruse Mocks

David Rawson
  • 20,912
  • 7
  • 88
  • 124
  • I did not get it. In the examples, I have givens simple functions to understand easily. In my actual code, they are complex functions some included I/O calls too. – Raghu Molabanti Apr 12 '20 at 03:29