Is it possible using Mockito and optionally Powermock to mock a superclass S
such that any calls to the superclass to S
(including calls to the S()
constructor) are mocked? So using the below example, if I replace S
with MockS
using Mockito, will the call to super()
use the constructor in MockS
?
class S {
S() {
// Format user's hard drive, call 911, and initiate self-destruct
}
}
class T extends S {
T() {
super();
}
}
class Test {
@Mock private S mockS;
new T(); // T's call to super() should call the mock, not the destructive S.
}
I've seen questions about mocking individual methods in S
or mocking only calls to super()
, and read that this is unsupported, but it's not clear whether or not I can mock the entire superclass.
With my current tests, when I try to mock S
, T
's call to super()
calls the real implementation, not the mock.
To work around this apparent limitation, I refactored my code, replacing inheritance with delegation, and I think I've ended up with a better design anyhow since the inheritance wasn't really necessary.
The new code looks like this. Mind you the code for the question was simplified, so the real classes have much more functionality.
For those interested, using Guice and Android, the test looks more like this:
I think this is possible with PowerMock only if the method on the child is different from the method on the superclass (i.e., you cannot mock the parent method if the child overrides that method). For a little more detail, you can look at the relevant bug report.
For PowerMock, check out Suppressing Unwanted Behavior page to see if it will be enough for your needs.
After much digging around, I ended up using JMockit for these tricky cases. Before I moved on to JMockit, I tried stubbing out all the places exceptions were thrown using suppression. In the end, I needed to override some methods, and not just suppress them, so I ended up abandoning it.
Example usage for Android case:
First, you mock out your superclass using the
@MockClass
annotation:When activated, this class will replace the default constructor of
Activity
with$init()
, and replace theonCreate
method with the one above. WIth android, the unit under test is derived from Activity (in my sample code, it isHelloTestActivity
). The test class looks like this:The call
Mockit.setupMock(fakeActivity)
replaces the super class with my instance of the fake. With this usage, you can access internal state of your fake class as well. If you don't need to override any methods with custom functionality, you can use other methods available fromMockit
class.As rogerio pointed out in the comments below, mocking the
Activity
class is the bare minimum. The following code demonstrates this.The declaration
@Mocked Activity base;
causes all methods (excepting static initializers) ofActivity
class and its superclasses to be mocked in the tests defined inHelloActivityTest4
.What you can do is extract the 'dangerous' code in your superclass constructor into a non-private method, then use Mockito spy on your class T and override the behaviour in that extracted method.
This would of course violate encapsulation. Guava offers the VisibleForTesting annotation for such cases.