Mockito How to mock only the call of a method of the superclass

JavaMockito

Java Problem Overview


I'm using Mockito in some tests.

I have the following classes:

class BaseService {  
    public void save() {...}  
}
 
public Childservice extends BaseService {  
    public void save(){  
        //some code  
        super.save();
    }  
}   

I want to mock only the second call (super.save) of ChildService. The first call must call the real method. Is there a way to do that?

Java Solutions


Solution 1 - Java

If you really don't have a choice for refactoring you can mock/stub everything in the super method call e.g.

    class BaseService {

        public void validate(){
            fail(" I must not be called");
        }

        public void save(){
            //Save method of super will still be called.
            validate();
        }
    }

    class ChildService extends BaseService{

        public void load(){}

        public void save(){
            super.save();
            load();
        }
    }

    @Test
    public void testSave() {
        ChildService classToTest = Mockito.spy(new ChildService());

        // Prevent/stub logic in super.save()
        Mockito.doNothing().when((BaseService)classToTest).validate();

        // When
        classToTest.save();

        // Then
        verify(classToTest).load();
    }

Solution 2 - Java

No, Mockito does not support this.

This might not be the answer you're looking for, but what you're seeing is a symptom of not applying the design principle:

> Favor composition over inheritance

If you extract a strategy instead of extending a super class the problem is gone.

If however you are not allowed to change the code, but you must test it anyway, and in this awkward way, there is still hope. With some AOP tools (for example AspectJ) you can weave code into the super class method and avoid its execution entirely (yuck). This doesn't work if you're using proxies, you have to use bytecode modification (either load time weaving or compile time weaving). There are be mocking frameworks that support this type of trick as well, like PowerMock and PowerMockito.

I suggest you go for the refactoring, but if that is not an option you're in for some serious hacking fun.

Solution 3 - Java

Consider refactoring the code from ChildService.save() method to different method and test that new method instead of testing ChildService.save(), this way you will avoid unnecessary call to super method.

Example:

class BaseService {  
    public void save() {...}  
}

public Childservice extends BaseService {  
    public void save(){  
        newMethod();    
        super.save();
    }
    public void newMethod(){
       //some codes
    }
} 

Solution 4 - Java

I found a way to suppress the superclass method using PowerMockito. 3 simple steps need for this

  1. Use PowerMockito.suppress method and MemberMatcher.methodsDeclaredIn method to supress parent class method

  2. Second add Parent class in @PrepareForTest

  3. Run your test class with PowerMock ie add @RunWith(PowerMockRunner.class) above your test class.

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({BaseService.class})
    public class TestChildService(){
        
        @Spy
        private ChildService testChildServiceObj = Mockito.spy(new ChildService());
    
    	@Test
    	public void testSave(){
    		PowerMockito.suppress(MemberMatcher.methodsDeclaredIn(BaseService.class));
    		
    		//your further test code
    
            testChildServiceObj.save();
    	}
    }
    

Note: This will work only when the superclass method does not return anything.

Solution 5 - Java

Maybe the easiest option if inheritance makes sense is to create a new method (package private??) to call the super (lets call it superFindall), spy the real instance and then mock the superFindAll() method in the way you wanted to mock the parent class one. It's not the perfect solution in terms of coverage and visibility but it should do the job and it's easy to apply.

 public Childservice extends BaseService {
    public void save(){
        //some code
        superSave();
    }

    void superSave(){
        super.save();
    }
}

Solution 6 - Java

create a package protected (assumes test class in same package) method in the sub class that calls the super class method and then call that method in your overridden sub class method. you can then set expectations on this method in your test through the use of the spy pattern. not pretty but certainly better than having to deal with all the expectation setting for the super method in your test

Solution 7 - Java

Even if i totally agree with iwein response (

> favor composition over inheritance

), i admit there are some times inheritance seems just natural, and i don't feel breaking or refactor it just for the sake of a unit test.

So, my suggestion :

/**
 * BaseService is now an asbtract class encapsulating 
 * some common logic callable by child implementations
 */
abstract class BaseService {  
    protected void commonSave() {
		// Put your common work here
	}
	
	abstract void save();
}

public ChildService extends BaseService {  
    public void save() {
        // Put your child specific work here
		// ...
		
        this.commonSave();
    }  
}

And then, in the unit test :

	ChildService childSrv = Mockito.mock(ChildService.class, Mockito.CALLS_REAL_METHODS);
    
    Mockito.doAnswer(new Answer<Void>() {
        @Override
        public Boolean answer(InvocationOnMock invocation)
                throws Throwable {
			// Put your mocked behavior of BaseService.commonSave() here
            return null;
        }
    }).when(childSrv).commonSave();

	childSrv.save();
	
	Mockito.verify(childSrv, Mockito.times(1)).commonSave();

	// Put any other assertions to check child specific work is done

Solution 8 - Java

You can do this with PowerMockito and replace behavior only of the parent class method with continuing testing the child's class method. Even when the method is returning some value, lets say a string, you can do something like this:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ BaseService.class })
public class TestChildService() {

    private BasicService basicServiceObj;
    private ChildService testee;

    @Before
    public void init() {
        testee = new ChildService();
        basicServiceObj = PowerMockito.spy(new BaseService());
        PowerMockito.doReturn("Result").when(basicServiceObj, "save", ... optionalArgs);
    }

    @Test
    public void testSave(){
        testee.save();
    }
}

If you are returning nothing (void) then instead of doReturn you can use doNothing. Add some optionalArgs if the method have some arguments, if not, then skip that part.

Solution 9 - Java

The reason is your base class is not public-ed, then Mockito cannot intercept it due to visibility, if you change base class as public, or @Override in sub class (as public), then Mockito can mock it correctly.

public class BaseService{
  public boolean foo(){
    return true;
  }
}

public ChildService extends BaseService{
}

@Test
@Mock ChildService childService;
public void testSave() {
  Mockito.when(childService.foo()).thenReturn(false);

  // When
  assertFalse(childService.foo());
}

Solution 10 - Java

There is simple approach that works for most of cases. You can spy your object and stub the method you want to mock.

Here is an example:

MyClass myObjectSpy = Mockito.spy(myObject);
org.mockito.Mockito.doReturn("yourReturnValue").when(mySpyObject).methodToMock(any()..);

So, when you test your object, you can use myObjectSpy and when methodToMock is called, it will overwrite the normal behavior by a mock method.

This code for a method with return. In case you have a void method you can use doNothing instead.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmadaView Question on Stackoverflow
Solution 1 - JavajgonianView Answer on Stackoverflow
Solution 2 - JavaiweinView Answer on Stackoverflow
Solution 3 - JavaMohammed MisbahuddinView Answer on Stackoverflow
Solution 4 - JavaSumit RaneView Answer on Stackoverflow
Solution 5 - JavaRubasaceView Answer on Stackoverflow
Solution 6 - JavaLukeView Answer on Stackoverflow
Solution 7 - Javadams50View Answer on Stackoverflow
Solution 8 - JavaSašaView Answer on Stackoverflow
Solution 9 - JavazoufeiyyView Answer on Stackoverflow
Solution 10 - JavaRafael BorjaView Answer on Stackoverflow