Can I make fun of the superclass constructor with Mockito / Powermock?

Is it possible to use Mockito and possibly Powermock to make fun of the superclass S in such a way that any calls to the superclass before S (including calls to the S() constructor) are mocked? So, using the example below, if I replaced S with MockS using Mockito, would super() call the constructor in MockS ?

 class S { S() { // Format user hard drive, call 911, and initiate self-destruct } } class T extends S { T() { super(); } } class Test { @Mock private S mockS; new T(); // T call to super() should call the mock, not the destructive S. } 

I saw questions about bullying individual methods in S or bullying only on super() , and read that this is unsupported, but it is unclear if I can make fun of the whole superclass.

With my current tests, when I try to make fun of S , T calling super() calls the real implementation, not the layout.

+7
source share
3 answers

To get around this obvious limitation, I reorganized my code by replacing inheritance with delegation , and I think I got a better design anyway, since inheritance was not really necessary.

The new code is as follows. Keep in mind that the code for the question has been simplified, so real classes have much more functionality.

 class S { S() { // Format user hard drive, call 911, and initiate self-destruct } } class T { T(S s) {} // Now T "has an S" instead of "is an S" } class Test { @Mock private S mockS; new T(s); // T call to super() should call the mock, not the destructive S. } 

For those who want, using Guice and Android, the test looks something like this:

 class T { T(Activity activity, S s) {} } class Test { @Mock Activity activity; @Mock S mockS; injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Activity.class).toInstance(activity); bind(S.class).toInstance(mockS); }} ); T t = injector.getInstance(T.class); } 
+4
source

I think this is possible with PowerMock only if the child’s method is different from the method of the superclass (i.e. you cannot mock the parent method if the child overrides this method). For more information, you can see the corresponding error report .

For PowerMock, check out “Suppressing unwanted behavior page” to see if this is enough for your needs.


After much digging, I ended up using JMockit for these complex cases. Before I switched to JMockit, I tried to align all places except that exceptions were thrown. In the end, I needed to override some methods, not just suppress them, so I left it.

Usage example for Android case:

First, you mock your superclass using the @MockClass annotation:

 @MockClass(realClass = Activity.class, instantiation = PerMockedInstance) public class FakeActivity { public Bundle mSavedInstanceState; @Mock public void $init() {} @Mock public void onCreate(Bundle savedInstanceState) { mSavedInstanceState = savedInstanceState; } } 

When activated, this class will replace the default constructor of Activity with $init() and replace the onCreate method with the one above. WIth android, the test block is obtained from Activity (in my code example, this is HelloTestActivity ). The testing class is as follows:

 public class HelloTestActivityTest3 extends AndroidTest { @Tested HelloTestActivity activity; FakeActivity fakeActivity = new FakeActivity(); @Before public void setupMocks() { Mockit.setUpMock(fakeActivity); } @Test public void onCreate_bundle(@Mocked Bundle savedInstanceState) { // Try to access out-of-band information from the fake activity.onCreate(savedInstanceState); assertSame(savedInstanceState, fakeActivity.mSavedInstanceState); } } 

Calling Mockit.setupMock(fakeActivity) replaces the superclass with my fake instance. With this use, you can also access the internal state of your fake class. If you do not need to override any methods using special functions, you can use other methods available from the Mockit class.

As Rogerio noted in the comments below, the mockery of the Activity class is minimal. The following code demonstrates this.

 public class HelloTestActivityTest4 { @Tested HelloTestActivity activity; @Mocked Activity base; @Test public void testOnCreate() throws Exception { // Just make sure "Stub!" exception is not thrown. activity.onCreate(null); } } 

@Mocked Activity base; declaration @Mocked Activity base; forces all methods (except static initializers) of the Activity class and its superclasses to be mocked in the tests defined in HelloActivityTest4 .

+3
source

What you can do is extract the “dangerous” code in the superclass constructor into a non-private method, and then use Mockito for your class T and override the behavior in this extracted method.

This, of course, will break encapsulation. Guava offers a VisibleForTesting annotation for such cases.

+1
source

All Articles