How can I test a method which invoke protected (un

2019-03-30 07:43发布

问题:

I'm stuck in a very weird case. I have some specific code which I need to test. Here it is:

public class A {

    /*
     * The real method of real class is so big that I just don't want to test it.
     * That's why I use throwing an exception.
     */
    protected void method(Integer result) {
        throw new RuntimeException("Oops!");
    }

    protected <T> T generifiedMethod(String s, T type) {
        throw new RuntimeException("Oops!");
    }

    protected void mainMethod(Integer value) {
        throw new RuntimeException("Oops!");
    }
}

I also have a child class:

public class B extends A {

    @Override
    protected void mainMethod(Integer value) {
        if (value == 100500) {
            Integer result = super.generifiedMethod("abc", 100);
            super.method(result);
        }
        super.mainMethod(value);
    }
}

I need to cover the child class with tests.

I was trying a lot of combinations with PowerMockito, but none of them can verify invocation of protected methods of parent class. Also, I have a restriction on use only Mockito, PowerMockito and TestNG.

Here is my test code (one of variants):

@Test
public void should_invoke_parent_logic_methods_of_A_class() throws Exception {

    /* Given */
    A aSpy = PowerMockito.spy(new A());

    PowerMockito.doReturn(250).when(aSpy, "generifiedMethod", "abc", 100);
    PowerMockito.doNothing().when(aSpy, "method", 250);
    PowerMockito.suppress(method(A.class, "mainMethod", Integer.class));

    /* When */
    aSpy.mainMethod(100500);

    /* Then */
    /**
     * Here I need to verify invocation of all methods of class A (generifiedMethod(), method(),
     * and mainMethod()). But I don't need them to be invoked because their logic is unwanted
     * to be tested in case of tests for class B.
     */
}

I would be appreciate for any suggestions how to test class B. Thanks.

Update

If I add into Then section this code

Mockito.verify(aSpy, times(3)).mainMethod(100500);
Mockito.verify(aSpy, times(1)).generifiedMethod("abc", 100);
Mockito.verify(aSpy, times(1)).method(250);

It gives me the following error message:

Wanted but not invoked:
a.generifiedMethod("abc", 100);

回答1:

Did you consider variant to change design of your classes and use composition instead of inheritance ? Then you will be able to just mock / spy instance of class A and inject it to instance of class B. In such case you will be able to configure whatever behavior that you need.

I really not sure doCallRealMethod() will make trick for you, cause you have option to mock method or invoke real one, but not both simultaneously.



回答2:

If I understood you correctly, you want to test the method 'mainMethod' from class B. So, you should mock an object from class B, and not one from class A. Try this:

/* Given */
B b = Mockito.mock(B.class);

//use this to skip the method execution
Mockito.doNothing().when(b).generifiedMethod(Mockito.anyString(), Mockito.any());
Mockito.doNothing().when(b).method(Mockito.anyInt());

//or use this to return whatever you want when the methods are called
Mockito.doReturn(new Object()).when(b).method(Mockito.anyInt());
Mockito.doReturn(new Object()).when(b).generifiedMethod(Mockito.anyString(), Mockito.any());

Then you can call the mainMethod from B, already knowing what the other methods will return.



回答3:

I think you are in a case where you should not use a mock library, but revert to the old specialized stub mode.

You have to create a specialized A that has the following functionnalities :

  • note that methods method, generifiedMethod and mainMethod have been called
  • reset the above indicators
  • give read access to the above indicators.

You can then construct your tests with Junit or TestNG. The main problem that remain IMHO is that you probably will have to use a custom build procedure to load the A stub class and not the real A.