Mockito How to Mock Only the Call of a Method of the Superclass

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

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.

Mocking Superclass method call

You should not be mocking this call. You should not have inheritance here at all. If class A needs to call method b() which is tested elsewhere and you don't care about what it does, it means that method b() should be in class B which is a dependency of class A - and then you can do dependency injection and mock B.

If this is some legacy code that you cannot change, one of interesting strategies of refactoring it is making a new method in class A and overriding it in your test (by either extending A or spying on this method):

class A extends B {
private void b() {
// something here
super_b();
}
protected void super_b() {
super.b();
}
}

Can I mock a super class method call?

Well, you can if you want to. I don't know if you are familiar with JMockit, go check it out. The current version is 0.999.17 In the mean time, let's take a look at it...

Assume the following class hierarchy:

public class Bar {
public void bar() {
System.out.println("Bar#bar()");
}
}

public class Foo extends Bar {
public void bar() {
super.bar();
System.out.println("Foo#bar()");
}
}

Then, using JMockit in your FooTest.java you can validate that you're actually making a call to Bar from Foo.

@MockClass(realClass = Bar.class)
public static class MockBar {
private boolean barCalled = false;

@Mock
public void bar() {
this.barCalled = true;
System.out.println("mocked bar");
}
}

@Test
public void barShouldCallSuperBar() {
MockBar mockBar = new MockBar();
Mockit.setUpMock(Bar.class, mockBar);

Foo foo = new Foo();
foo.bar();

Assert.assertTrue(mockBar.barCalled);

Mockit.tearDownMocks();
}

Mockito Mock superclass method on a dependency

Probably you've messed something in a setup.
I've tried to reproduce your case and it works in my setup:

package com.example.demo;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.when;

enum Result {
VALID, INVALID;
}
abstract class Base {
protected abstract void method1();

public Result validate() {
return Result.INVALID;
}
}
class Dependency extends Base {
public void method1() {

}
}
class Subject {
private Dependency dep;

public Subject(Dependency d) {
this.dep = d;
}

public Result test() {
return dep.validate();
}
}

public class SampleTest {

private Dependency dep = Mockito.mock(Dependency.class);

private Subject subject;


@Test
public void testMe() {
subject = new Subject(dep);
when(dep.validate()).thenReturn(Result.VALID); //validate is a super class method

Result result = subject.test();
assertThat(result, equalTo(Result.VALID));
}

}

All-in-all Mockito should support such a use case without any issues.

I suspect that its something with "initMocks"/ or maybe @Mock annotation that requires a special runner/extension if you're on JUnit 5, which is not shown in a question.

I've intentionally avoided these "advanced" features in the example above to create a minimal reproducible example. Please check this code and update whether it works for you.

P.S.: used Mockito-core:3.3.3 for the test

How to test when Superclass method is called?

Judging from your test, I imagine you want to verify() that your configuration's dataSource() method is being called. If so, you probably want something like the following:

@Test
void dataSource() {
PsqlConfiguration ch = Mockito.spy(new PsqlConfiguration());
DataSource mockDs = Mockito.mock(DataSource.class);
Mockito.doReturn(mockDs).when(ch).dataSource();
}

The reason you are getting the error is that the AbstractCloudConfig class's dataSource() method returns a DataSource, not void. You can only use doNothing() to mock void methods.

If you really want to mock the superclass, you could do something like this:

@Test
void dataSource() {
PsqlConfiguration ch = Mockito.spy(new PsqlConfiguration());
DataSource mockDs = Mockito.mock(DataSource.class);
ConnectionFactory mockFactory = Mockito.mock(ConnectionFactory.class);
Mockito.doReturn(mockDs).when(ch).dataSource();
Mockito.doReturn(mockFactory).when((AbstractCloudConfig)ch).connectionFactory();
ch.dataSource();
verify(ch).connectionFactory();
}

Note however, that this test is meaningless as it is written. Your configuration class does not execute any code that is not from your framework (as far as you have written, and the configuration is loaded by Spring). Tests should only test your own code, not the underlying framework (which has its own tests).


I see by your comment:

If i directly mock child class method then it works but code coverage is 0 percent so i thought to mock " connectionFactory().dataSource()" . So that it gives 100 percent code coverage, But it gives NullPointerException

That you're simply doing this for code coverage. This is not really a good practice, as:

  1. You are bloating the code base to write tests for something that is not within your code's responsibility.
  2. 100% coverage does not mean that the code base is bug-free.

Now, if your configuration source also did something with the dataSource (aside from just retrieving it), you might write a test for that to assert that everything in your code uses it correctly. However, as written, it does not really help in any way.



Related Topics



Leave a reply



Submit