Jude Niroshan
Jude Niroshan

Reputation: 4460

Mock inherited method in Mockito Java

My class structure is as follows:

public class MyParentClass {

    void doSomethingParent() {
        System.out.println("something in parent");
    }
}

public class MyClass extends MyParentClass {

    protected String createDummyRequest(Holder myHolder) {
        //...
        super.doSomethingParent();//I want to avoid this
        //...
        callingDB();
        return "processedOutput";
    }

    private void callingDB() {
        System.out.println("Calling to DB");
    }
}

Then my unit test:

public class UnitTest {

    public void testCreateDummyRequest() {
        //create my mock holder
        Holder mockHolder = new Holder();

        MyClass mockObj = Mockito.mock(MyClass.class);
        //mock doSomethingParent()
        //mock callingDB()

        //as mockObj is a fully mock, but I need to run my real method
        //Mockito.when(mockObj.createDummyRequest(mockHolder)).thenCallRealMethod();
        mockObj.createDummyRequest(mockHolder);
        //Problem: doSomethingParent() is getting called though I have mocked it
    }
}

How do I prevent the calling of the super.doSomethingParent() in my method? (method which I am writing my test)

Upvotes: 6

Views: 16816

Answers (4)

Thiago Cavalcanti
Thiago Cavalcanti

Reputation: 523

Here is how it can be done

public class BaseController {

     public void method() {
          validate(); // I don't want to run this!
     }
}
public class JDrivenController extends BaseController {
    public void method(){
        super.method()
        load(); // I only want to test this!
    }
}

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

    // Prevent/stub logic in super.method()
    Mockito.doNothing().when((BaseController)spy).validate();

    // When
    spy.method();

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

Source: https://blog.jdriven.com/2013/05/mock-superclass-method-with-mockito/

Upvotes: 2

Poger
Poger

Reputation: 1937

I found another approach, which turned out to be very useful in my case.

In the case I had, I needed to create a new class extending another, which included a very complex (legacy code) protected final method. Due to the complexity, it wasn't really possible to refactor to use composition, so here's what I came up with.

Let's say I have the following:

abstract class Parent {

    public abstract void implementMe();

    protected final void doComplexStuff( /* a long parameter list */) {
        // very complex legacy logic
    }

}

class MyNewClass extends Parent {

    @Override
    public void implementMe() {
        // custom stuff
        doComplexStuff(/* a long parameter list */); // calling the parent
        // some more custom stuff
    }

}

Here's how I rearranged this code:

abstract class Parent {

    public abstract void implementMe();

    protected final void doComplexStuff( /* a long parameter list */) {
        // very complex legacy logic
    }

}

interface ComplexStuffExecutor {
    void executeComplexStuff(/* a long parameter list, matching the one from doComplexStuff */);
}

class MyNewClass extends Parent {

    private final ComplexStuffExecutor complexStuffExecutor;

    MyNewClass() {
        this.complexStuffExecutor = this::doComplexStuff;
    }


    MyNewClass(ComplexStuffExecutor complexStuffExecutor) {
        this.complexStuffExecutor = complexStuffExecutor;
    }

    @Override
    public void implementMe() {
        // custom stuff
        complexStuffExecutor.doComplexStuff(/* a long parameter list */); // either calling the parent or the injected ComplexStuffExecutor
        // some more custom stuff
    }

}

When creating instance of MyNewClass for "production" purposes, I can use the default constructor.

When writing unit tests, however, I'd use the constructor, where I can inject ComplexStuffExecutor, provide a mock there and only test my custom logic from MyNewClass, i.e.:

class MyNewClassTest {
    
    @Test
    void testImplementMe() {
        ComplexStuffExecutor complexStuffExecutor = Mockito.mock(ComplexStuffExecutor.class);
        doNothing().when(complexStuffExecutor).executeComplexStuff(/* expected parameters */);
        MyNewClass systemUnderTest = new MyNewClass(complexStuffExecutor);
        // perform tests
    }
}

At first glance, it seems like adding some boilerplate code just to make the code testable. However, I can also see it as an indicator of how the code should actually look like. Perhaps one day someone (who would find courage and budget ;) ) could refactor the code e.g. to implement the ComplexStuffExecutor with the logic from doComplexStuff from Parent, inject it into MyNewClass and get rid of inheritance.

Upvotes: 1

Nicktar
Nicktar

Reputation: 5575

With this class structure mocking and testing is real hard. If possible, I'd advice to change the structure as in mist cases a class structure that's hard to mock and test is equally hard to extend and maintain.

So if you could change your class structure to something similar to:

public class MyClass {

    private DoSomethingProvider doSomethingProvider;

    private DbConnector dbConnector;

    public MyClass (DoSomethingProvider p, DbConnector c) {
        doSomethingProvicer = p;
        dbConnector = c;
    }


    protected String createDummyRequest(Holder myHolder){
        //...
        doSomethingProvider.doSomethingParent();
        //...
        dbConnector.callingDB();
        return "processedOutput";
    }
}

Then you could easily create your instance with mocks of DoSomethingProvider and DbConnector and voila....

If you can't change your class structure you need to use Mockito.spy instead of Mockito.mock to stub specific method calls but use the real object.

public void testCreateDummyRequest(){
    //create my mock holder
    Holder mockHolder = new Holder();

    MyClass mockObj = Mockito.spy(new MyClass());

    Mockito.doNothing().when(mockObj).doSomething();

    mockObj.createDummyRequest(mockHolder);
}

Note: Using the super keyword prevents Mockito from stubbing that method call. I don't know if there is a way to stub calls to super. If possible (as in you didn't override the parent method in your class), just ommit the keyword.

Upvotes: 4

Sergii Bishyr
Sergii Bishyr

Reputation: 8641

I faced similar issue, so I find out that using spy() can hepld.

public class UnitTest {

  private MyClass myObj;

  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
    myObj= spy(new MyClass());
  }

  @Test
  public void mockedSuperClassMethod(){
    doNothing().when((MyParentClass )myObj).doSomethingParent();
    //...
  }
}

This approach works for me.

Upvotes: 7

Related Questions