3

I'm trying to use PowerMockito to create a spy of a final class but I keep getting the following error, even though I am using PowerMockito's spy() method in place of Mockito's:

java.lang.IllegalArgumentException: Cannot subclass final class class com.whoever.WidgetUploadClient

My test case looks something like this:

...
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.spy;

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(RobolectricTestRunner.class)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@PrepareForTest(WidgetUploadClient.class)
@Config(manifest=Config.NONE, sdk = 23)
public class WidgetUploadClientTest {
    @Test
    public void testUploadWidget() {
        WidgetMarshaller mockMarshaller = mock(WidgetMarshaller.class);
        WidgetUploadClient client = spy(new WidgetUploadClient(mockMarshaller)); // Exception thrown by spy()
        ...
    }
}

Shouldn't @PrepareForTest(WidgetUploadClient.class) and using PowerMockito's spy() method account for WidgetUploadClient being final?

I have also tried the alternative approach found in Robolectric's PowerMock guide: using RobolectricTestRunner or RobolectricGradleTestRunner as the test runner (@RunWith) with @Rule public PowerMockRule rule = new PowerMockRule(). When I do that, the test fails to run entirely and a different exception is thrown.

I am using PowerMock/PowerMockito 1.6.5, Robolectric 3.1 and Java 1.8.0_91-b14.

spaaarky21
  • 6,524
  • 7
  • 52
  • 65

2 Answers2

3

To get this working you have to understand what annotation @PrepareForTest does and make a little changes on your code:

The annotation in used to understand what class we're going to test and to prepare that class to mock static, final etc etc methods (so the methods that are not normally mockable with mockito) as normal methods.

After that you have to do this in your code:

WidgetMarshaller mockMarshaller = mock(WidgetMarshaller.class);
//Here you are doing correcly the mocking of the object

WidgetUploadClient client = new WidgetUploadClient(mockMarshaller);
//Here you have to add this line to create an object that will be spied

client = PowerMockito.spy(client);
//Here you simply spy your class

By the way there's another thing to remember, if you pass

@PrepareForTest(WidgetUploadClient.class)

to the class, you will be able to mock or spy just WidgetUploadClient class, so you have to pass two (or if you want more) parameters to the class using an array as parameter to the annotation, simply write this

@PrepareForTest({WidgetUploadClient.class, WidgetMarshaller.class})

Hope you get it working :D See you

  • Thanks for your response, Claudio. It's a nice example of using PowerMock. Although, I believe I am using PowerMock correctly, unless there's a misuse in my example that you can point out. I believe the real problem is using PowerMock specifically with Robolectric. From what I've found online, I believe my problem is a bug in Robolectric. – spaaarky21 Jun 30 '16 at 15:42
  • @spaaarky21 Did you try to pass an array to 'PrepareForTest' annotation as i suggest in my example? –  Jun 30 '16 at 16:10
  • That's a good thing to check but I believe that only `WidgetUploadClient` needs to be prepared since it's the only one that's `final`, unlike `WidgetMarshaller`. – spaaarky21 Jun 30 '16 at 19:58
  • For what i've readed online, probably the best thing to do (if you can, obviously) is to try to return to a version of Roboelectric that doesn't have that bug you're talking in your answer –  Jul 01 '16 at 07:26
0

I believe that I am using the APIs correctly but am experiencing by a bug that effects developers trying to use the combination of Robolectric and PowerMock. For reference, the bug can be tracked on Robolectric's issue tracker. The combination of libraries has been broken since at least January 2016 (currently ~6 months ago.)

spaaarky21
  • 6,524
  • 7
  • 52
  • 65