Search code examples
javaunit-testingmockito

Testing Delegate Method with Mockito


In the class like the below the only tests required around doActionOne() and doActionTwo() are to ensure that they delegate to doAction() with the correct parameters.

As the delegate doAction(String a, int b, boolean c) method requires a lot of set-up then any solution should prevent the real method being called.

public class ClassUnderTest {

    public void doActionOne(String a, int b) {
        doAction(a, b, true);
    }

    public void doActionTwo(String a, int b) {
        doAction(a, b, false);
    }

    public void doAction(String a, int b, boolean c) {
        //already tested
    }
}

Such a test would seem to require some kind of partial mock or spy however I am unable to get this correct.

The test should look something like the below although this approach doesn't work.

@Test
public void testDoActionOne(){
    ClassUnderTest cut = Mockito.mock(ClassUnderTest.class);

    //call the real method
    when(cut.doActionOne("A1", 1)).thenCallRealMethod();

    //test delegate called with correct params
    verify(cut, times(1)).doAction("A1", 1, true); //fails wanted but not invoked
}

Not sure if I need something like:

http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/AdditionalAnswers.html#delegatesTo(java.lang.Object)


Solution

  • I know of two ways to do this. One means creating an anonymous inner class in which you override the method you don't want to test. This does not involve any Mockito magic. The other means using a Mockito Spy object, this is a proxy to a real instance, allowing you to specify stubbed behavior for some methods, and let the other methods fall through to the real instance.

    Method 1, using an anonymous inner class:

    public class MyTest {
    
        private String a;
        private String b;
        private boolean c;
    
        private ClassUnderTest instance = new ClassUnderTest() {
            @Override
            public void doAction(String a, int b, boolean c) {
                MyTest.this.a = a; 
                MyTest.this.b = b;
                MyTest.this.c = c;
            }
        }
    
        public void test() {
            // SETUP
            String expectedA = "test value A";
            String expectedB = "test value B";           
    
            // CALL
            instance.doActionOne(expectedA, expectedB);
    
            // VERIFY
            assertEquals(expectedA, a);
            assertEquals(expectedB, b);
            assertTrue(c);
        }
    

    Method 2, using Mockito spy object:

    @RunWith(MockitoJUnitRunner.class)
    public class MyTest {
    
        @Spy
        private ClassUnderTest instance;
    
        public void test() {
            // SETUP
            String expectedA = "test value A";
            String expectedB = "test value B";
            doNothing().when(instance).doAction(expectedA, expectedB, true);
    
            // CALL
            instance.doActionOne(expectedA, expectedB);
    
            // VERIFY
            verify(instance, times(1)).doAction(expectedA, expectedB, true);
        }
    

    Because a spy is a Mockito controlled proxy you can also verify if methods were called on a spy, which is what you need here. You could also specify a stubbed return value for doAction (if it wasn't a void method):

            // SETUP
            doReturn("stubbed value").when(instance).doAction(expectedA , expectedB, true);