Clear guidelines for when an entry in @PrepareForTest is required?

977 views
Skip to first unread message

KARR, DAVID

unread,
May 6, 2014, 2:50:36 PM5/6/14
to powe...@googlegroups.com
I've gotten lots of PowerMock tests to work, and it often requires adding a @PrepareForTest annotation and putting various classes in that list. Frankly, I've never really been sure exactly what situations require putting a class into that list. I think I know some of them, but I often end up guessing at this, and probably have sometimes put more classes in that list than are required. It would help if I had a clear idea of when it's required.
winmail.dat

Johan Haleby

unread,
May 6, 2014, 3:15:37 PM5/6/14
to powe...@googlegroups.com
Usually you prepare the class the that contains the method. constructor or field you need to make testable. For example if a class has a static method you need to prepare it for test in order to mock it. System classes and mocking new instances is different in that you prepare the class calling the system class method or the constructor. For system classes the reason is that they are not possible to byte-code manipulate. For new instances to reason is that PowerMock detects the call to "new" which is made from the class calling the constructor and not the class being instantiated. 

Regards,
/Johan


On Tue, May 6, 2014 at 8:50 PM, KARR, DAVID <dk0...@att.com> wrote:
I've gotten lots of PowerMock tests to work, and it often requires adding a @PrepareForTest annotation and putting various classes in that list.  Frankly, I've never really been sure exactly what situations require putting a class into that list.  I think I know some of them, but I often end up guessing at this, and probably have sometimes put more classes in that list than are required.  It would help if I had a clear idea of when it's required.

--
You received this message because you are subscribed to the Google Groups "PowerMock" group.
To unsubscribe from this group and stop receiving emails from it, send an email to powermock+...@googlegroups.com.
To post to this group, send email to powe...@googlegroups.com.
Visit this group at http://groups.google.com/group/powermock.
For more options, visit https://groups.google.com/d/optout.

KARR, DAVID

unread,
May 6, 2014, 6:31:03 PM5/6/14
to powe...@googlegroups.com

Is it also the case that if you mock static methods in a class, you have to prepare both the class with the static methods AND the class calling those static methods?

Johan Haleby

unread,
May 7, 2014, 12:48:03 AM5/7/14
to powe...@googlegroups.com
No usually not. If it's a normal class then only the class containing the static method needs to be prepared. If it's a system class then only the class calling the static method needs to be prepared.

/Johan

KARR, DAVID

unread,
May 7, 2014, 11:17:12 AM5/7/14
to powe...@googlegroups.com

For some background, I’m currently looking at a test which we’ll call “FooTest”, which is a test for class “Foo”.  The “Foo” class uses a “LoggingUtil” class.  The test suppresses the static initialization of “LoggingUtil” and mocks all of its static methods (it doesn’t specify any behavior for them).  The test also suppresses the static initialization of three other related classes.  In at least one of the tests, it needs to mock the ResourceBundle class, which apparently requires a slightly different way of specifying behavior.

 

In order for the tests to all not fail with errors (not addressing all test assertions as of yet), I have to have this:

 

    @PrepareForTest({Foo.class, LoggingUtil.class})

 

Note that I have the “Foo” class in this list, the class under test.  I’m not mocking any of the methods of Foo.

 

Here’s an elided excerpt from a relevant test method:

        when(resourceBundleService.getResourceBundle(anyString())).thenReturn(rsb);

        PowerMockito.doReturn("This item will be ship on {0}").when(rsb).getString(anyString());

 

The “rsb” is a ResourceBundle object (which is an instance of a “system class”).  The “resourceBundleService” is a mock object in my test class.

 

The following is an elided excerpt from a method called by the “Foo” class during the execution of the test method:

            ResourceBundle bundle = getResourceBundleService().getResourceBundle(pLocale);

            translatorMsg = bundle != null ? bundle.getString(getDeliveryPromiseKeyMap().get(pDlvryPromiseKey)) : null;

 

When I step through this last block of code, the “bundle” I get from the first line is the mock object specified from the first “when” in the first block.  This is fine.  When I step over the second line in this last block, I get the string specified in the second line of the first block. HOWEVER, if my “@PrepareForTest” list does NOT include “Foo.class”, the second line in the second block fails with a “MissingResourceException”, which is thrown from the body of “ResourceBundle.getObject()”, which is called by “ResourceBundle.getString()”.  Note that because I removed “Foo.class” frmo the @PrepareForTest list, it then failed to mock the “getString()” method of ResourceBundle.  When I add “Foo.class” back in and rerun the test, it works.

 

So, this appears to violate your guidelines.  It appears as if mocking system classes requires “preparing for test” the class making those calls, but that’s just my theory.  Do you have an alternative explanation?

Johan Haleby

unread,
May 9, 2014, 12:43:24 AM5/9/14
to powe...@googlegroups.com
Yes, mocking a system class (such as ResourceBundle) requires you to prepare the class calling the system class. Or what exactly do you mean is the difference from what I replied earlier? Perhaps what's confusing you is that it's possible to mock non-final methods in non-final system classes? That works perfectly well with other mocking libraries as well. But in this case "getString" is final which means that you have to use PowerMock byte-code manipulation. Since it's not possible for PowerMock to alter the byte-code of a system class to remove the final modifier PowerMock instead needs to modify the outgoing call to ResourceBundle from Foo and capture the method call. Thus you need to prepare Foo for test in this case.

Regards,
/Johan

KARR, DAVID

unread,
May 10, 2014, 1:47:38 PM5/10/14
to powe...@googlegroups.com

Also, can you more clearly define what you mean by “system class”?  Does “system class” simply mean anything defined in the bootclasspath, or is it based on the package beginning with “java.*” or “javax.*” (there are other packages besides those in the bootclasspath).

 

Also, can you provide a little bit of background on why you have to deal with “system” classes differently?  I have some ideas, but it would be good to have this spelled out clearly.  The PowerMock documentation doesn’t say anything about this that I can see.

Johan Haleby

unread,
May 10, 2014, 2:30:47 PM5/10/14
to powe...@googlegroups.com
Hi, 

If I remember it correctly I think I've summarized what you are asking in this blog.

Regards,
/Johan
Reply all
Reply to author
Forward
0 new messages