PrepareForTest together with whenNew

1,746 views
Skip to first unread message

Alex

unread,
Nov 14, 2010, 5:47:51 PM11/14/10
to PowerMock
Hi.

I just coded a test where I'm making use of whenNew(...). The tests
I've written pass when I execute them individually. However, when I
execute the whole test case only the first test passes and all other
tests fail. Here I had my @PrepareForTest annotation at the class
level.

After one hour of searching where the error in my code is I found out,
that it works if I put the @PrepareForTest to each test individually.
I think this is due to a bug in PowerMock?

I'll now attach a portion of my test should you want to deeper
investigate.

@PrepareForTest(fullyQualifiedNames =
{ "org.sunstormstudios.engine.common.internal.GlobalEngineConfiguration",
"org.sunstormstudios.engine.base.GameStage",
"org.sunstormstudios.engine.game.Game",
"org.sunstormstudios.engine.game.GameRunner",
"org.sunstormstudios.engine.rendering.internal.Rendering",
"org.sunstormstudios.engine.input.internal.Keyboard",
"org.sunstormstudios.engine.audio.internal.Audio" })
public class GameTest extends EngineTest {

private static final String GAME_TITLE = "Test Game";

private GlobalEngineConfiguration configuration;

private Rendering rendering;

private Keyboard keyboard;

private Audio audio;

private GameRunner gameRunner;

private Game game;

@Before
public void setUp() throws Exception {
configuration = PowerMockito.mock(GlobalEngineConfiguration.class);
when(configuration.getDisplayResolutionWidth()).thenReturn(1024);
when(configuration.getDisplayResolutionHeight()).thenReturn(768);
PowerMockito.mockStatic(GlobalEngineConfiguration.class);

when(GlobalEngineConfiguration.getInstance()).thenReturn(configuration);

rendering = PowerMockito.mock(Rendering.class);

PowerMockito.whenNew(Rendering.class).withNoArguments().thenReturn(rendering);

keyboard = PowerMockito.mock(Keyboard.class);

PowerMockito.whenNew(Keyboard.class).withNoArguments().thenReturn(keyboard);

audio = PowerMockito.mock(Audio.class);

PowerMockito.whenNew(Audio.class).withNoArguments().thenReturn(audio);

gameRunner = PowerMockito.mock(GameRunner.class);

PowerMockito.whenNew(GameRunner.class).withNoArguments().thenReturn(gameRunner);

game = new Game(GAME_TITLE);
}

@Test
public void startGameInstantlyFinished() throws Exception {
when(gameRunner.update()).thenReturn(true);

GameStage gameStage = PowerMockito.mock(GameStage.class);
game.startGame(gameStage);

InOrder inOrder = inOrder(gameRunner);
inOrder.verify(gameRunner).switchToGameStage(gameStage);
inOrder.verify(gameRunner).update();
inOrder.verify(gameRunner).release();

verify(keyboard).release();
verify(rendering).release();
verify(audio).release();
}

@Test
public void startGameLoopOnceDisplayVisible() throws Exception {
when(rendering.isDisplayVisible()).thenReturn(true);
when(gameRunner.update()).thenReturn(false, true);

GameStage gameStage = PowerMockito.mock(GameStage.class);
game.startGame(gameStage);

InOrder inOrder = inOrder(gameRunner);
inOrder.verify(gameRunner).switchToGameStage(gameStage);
inOrder.verify(gameRunner).update();
inOrder.verify(gameRunner).render();
inOrder.verify(gameRunner).update();
inOrder.verify(gameRunner).release();
}

}

Johan Haleby

unread,
Nov 15, 2010, 2:28:16 AM11/15/10
to powe...@googlegroups.com
Hi, 

It could be that you have unfinished expectation setups (i.e. unfinished when(..) statements) in which case PowerMock could potentially maintain state from a previous test in a new test. This should not happen though because we try to clean up for each test method but it could be something we've missed. What error message do you get?

/Johan


--
You received this message because you are subscribed to the Google Groups "PowerMock" group.
To post to this group, send email to powe...@googlegroups.com.
To unsubscribe from this group, send email to powermock+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/powermock?hl=en.


Alex

unread,
Nov 15, 2010, 5:48:13 AM11/15/10
to PowerMock
The error that I get is that for some of the verify methods PowerMock
says there were no invocations or that there were too much
invocations. However that is not true (as if I execute the tests
individually it works).

I think that the problem is caused by the following:

Byte code manipulation is used to modify the constructor calls (I
don't know it but i guess it). The @PrepareForTest annotation triggers
the byte code manipulation. If it's at the class level the byte code
manipulation is done exactly once before all tests are executed.
However, my setUp() method is invoked before each test. In this
setUp() method I always create new mocks for the current test. These
new mocks should be returned then by the modified constructor call.
This works for the first test but then for the second test it is not
updated with the new mock object created in the setUp() method.
Therefore it still returns the old mock with all it's state and thus
he has invalid verify() counts. If however I put the @PrepareForTest
at each method individually the byte code manipulation gets reset.

That's very roughly how I think this issue happens without knowing how
PowerMock is actually implemented.

Ok, let's quickly verify what I just said ^ ^ Yep, it is definately
the same mock that is injected for the modified constructor. The
second time the setUp() method is invoked the whenNew call seems to
have no effect. Here is the code I quickly assembled to test it:

@PrepareForTest(fullyQualifiedNames =
{ "org.sunstormstudios.engine.game.Game",
"org.sunstormstudios.engine.rendering.internal.Rendering",
"org.sunstormstudios.engine.audio.internal.Audio",
"org.sunstormstudios.engine.input.internal.Keyboard" })
public class GameTest extends EngineTest {

private Rendering rendering;

@Before
public void setUp() throws Exception {
rendering = PowerMockito.mock(Rendering.class);

PowerMockito.whenNew(Rendering.class).withNoArguments().thenReturn(rendering);
}

@Test
public void test1() {
System.out.println(Game.RENDERING);
}

@Test
public void test2() {
System.out.println(Game.RENDERING);
}

}

The output is the following:

Mock for Rendering, hashCode: 1119029232
Mock for Rendering, hashCode: 1119029232


On 15 Nov., 08:28, Johan Haleby <johan.hal...@gmail.com> wrote:
> Hi,
>
> It could be that you have unfinished expectation setups (i.e. unfinished
> when(..) statements) in which case PowerMock could potentially maintain
> state from a previous test in a new test. This should not happen though
> because we try to clean up for each test method but it could be something
> we've missed. What error message do you get?
>
> /Johan
>
> On Sun, Nov 14, 2010 at 11:47 PM, Alex
> <alexander.weickm...@googlemail.com>wrote:
> > powermock+...@googlegroups.com<powermock%2Bunsu...@googlegroups.com>
> > .

Johan Haleby

unread,
Nov 15, 2010, 6:20:21 AM11/15/10
to powe...@googlegroups.com
Hi, 

Thanks for your investigation, very good! Does this only happen when you're using "whenNew" or does it also happens on standard "when"? And also, how do you do your verification?

/Johan

To unsubscribe from this group, send email to powermock+...@googlegroups.com.

Alex

unread,
Nov 15, 2010, 7:01:40 AM11/15/10
to PowerMock
It does only happen with whenNew. Normal when works:

@PrepareForTest(Rendering.class)
public class GameTest extends EngineTest {

private Rendering rendering;

@Before
public void setUp() throws Exception {
rendering = PowerMockito.mock(Rendering.class);
PowerMockito.when(rendering.getDisplayHeight()).thenReturn(new
Random().nextInt());
}

@Test
public void test1() {
System.out.println(rendering.getDisplayHeight());
}

@Test
public void test2() {
System.out.println(rendering.getDisplayHeight());
}

}

Gives the output:
74680897
1613028554

I don't know exactly what you mean by "how do you do your
verification?". You should be able to see the verify calls in the
example I posted at beginning of the thread, for example:

InOrder inOrder = inOrder(gameRunner);

inOrder.verify(gameRunner).switchToGameStage(gameStage);
inOrder.verify(gameRunner).update();
inOrder.verify(gameRunner).release();

verify(keyboard).release();
verify(rendering).release();
verify(audio).release();


I've taken a quick look into the code and I think this method of
DefaultConstructorExpectationSetup is causing the problem as it checks
if the class to inject constructors for has been mocked before and if
so just uses the one from the repository:

@SuppressWarnings({ "unchecked", "rawtypes" })
private static <T> OngoingStubbing<T>
createNewSubsituteMock(Class<T> type, Class<?>[] parameterTypes,
Object... arguments) throws Exception {
if (type == null) {
throw new IllegalArgumentException("type
cannot be null");
}

final Class<T> unmockedType = (Class<T>)
WhiteboxImpl.getUnmockedType(type);
if (parameterTypes == null) {

WhiteboxImpl.findUniqueConstructorOrThrowException(type, arguments);
} else {
WhiteboxImpl.getConstructor(unmockedType,
parameterTypes);
}

/*
* Check if this type has been mocked before
*/
NewInvocationControl<OngoingStubbing<T>>
newInvocationControl = (NewInvocationControl<OngoingStubbing<T>>)
MockRepository
.getNewInstanceControl(unmockedType);
if (newInvocationControl == null) {
InvocationSubstitute<T> mock =
MockCreator.mock(InvocationSubstitute.class, false, false, null, null,
(Method[]) null);
newInvocationControl = new
MockitoNewInvocationControl(mock);
MockRepository.putNewInstanceControl(type,
newInvocationControl);

MockRepository.addObjectsToAutomaticallyReplayAndVerify(WhiteboxImpl.getUnmockedType(type));
}

return
newInvocationControl.expectSubstitutionLogic(arguments);
}




On 15 Nov., 12:20, Johan Haleby <johan.hal...@gmail.com> wrote:
> Hi,
>
> Thanks for your investigation, very good! Does this only happen when you're
> using "whenNew" or does it also happens on standard "when"? And also, how do
> you do your verification?
>
> /Johan
>
> On Mon, Nov 15, 2010 at 11:48 AM, Alex
> <alexander.weickm...@googlemail.com>wrote:
> > <powermock%2Bunsu...@googlegroups.com<powermock%252Buns...@googlegroups.com>

Johan Haleby

unread,
Nov 15, 2010, 9:22:28 AM11/15/10
to powe...@googlegroups.com
Just to make it clear, are you using the PowerMockRunner (i.e. do you have the @RunWith(PowerMockRunner.class) at the class-level of your test)? Also I'm not sure if InOrder works with PowerMock, we haven't added support for it yet (see issue 279). 

/Johan

To unsubscribe from this group, send email to powermock+...@googlegroups.com.

Alex

unread,
Nov 16, 2010, 7:43:13 AM11/16/10
to PowerMock
Yes, I use the @RunWith(PowerMockRunner.class) annotation at class
level of the super class EngineTest. The problem also occurs without
the InOrder verification.

On 15 Nov., 15:22, Johan Haleby <johan.hal...@gmail.com> wrote:
> Just to make it clear, are you using the PowerMockRunner (i.e. do you have
> the @RunWith(PowerMockRunner.class) at the class-level of your test)? Also
> I'm not sure if InOrder works with PowerMock, we haven't added support for
> it yet (see issue 279<http://code.google.com/p/powermock/issues/detail?id=279>
> ).
>
> /Johan
> ...
>
> Erfahren Sie mehr »

Alex

unread,
Nov 16, 2010, 10:55:31 AM11/16/10
to PowerMock
Are you reproduce the error in a simple test?

Writing the whole PrepareForTest thing to each method makes stuff very
confusing so I handle the situation now by making changing the setup.
I create my mocks in the @BeforeClass and then reset them in the
@Before method. It's better than writing the PrepareForTest annotation
at each method.

@BeforeClass
public static void setUpMocks() throws Exception {
rendering = PowerMockito.mock(Rendering.class);

PowerMockito.whenNew(Rendering.class).withNoArguments().thenReturn(rendering);

keyboard = PowerMockito.mock(Keyboard.class);

PowerMockito.whenNew(Keyboard.class).withNoArguments().thenReturn(keyboard);

audio = PowerMockito.mock(Audio.class);

PowerMockito.whenNew(Audio.class).withNoArguments().thenReturn(audio);
}

@Before
public void setUp() throws Exception {
reset(rendering, keyboard, audio);

game = new Game(GAME_TITLE);
gameRunner = PowerMockito.mock(GameRunner.class);
Whitebox.setInternalState(game, GameRunner.class, gameRunner);
> ...
>
> Erfahren Sie mehr »

Johan Haleby

unread,
Nov 16, 2010, 3:36:01 PM11/16/10
to powe...@googlegroups.com
Yes that's not pretty. Could you help us out by creating a small example
that demonstrates the issue?

/Johan

>> Erfahren Sie mehr �

ilinca

unread,
Nov 19, 2010, 3:36:11 AM11/19/10
to PowerMock
I'm having the same issue, although I'm not using whenNew or anything
else. My tests basically look like this:

@RunWith(PowerMockRunner.class)
@PrepareForTest(SUKAT.class)
public class AccountFacadeImplTest extends ServiceLocalTestBase{

protected void onSetUpInTransaction() throws Exception {
super.onSetUpInTransaction();
accountFacade = null;
try {
accountFacade = ServiceLocator.instance().getAccountFacade();
} catch (Throwable exception) {
System.err.println("<"+exception.getClass().getName()+"> :
"+exception.getMessage());
exception.printStackTrace();
}
}

@Test
public void testGetUser() throws Exception{
mockStatic(SUKAT.class);
SUKAT sukat = mock(SUKAT.class);
when(SUKAT.newInstance(SUKAT_REF)).thenReturn(sukat);

when(sukat.findUserByUid("myUid")).thenReturn(new
SearchResult("somename", new Object(), new BasicAttributes()));
assertEquals("somename" accountFacade.getUser("myUid").getName());

}

This works for the first couple of tests, but at some point, after a
seemingly arbitrary amount of tests, the annotation
PrepareForTest(SUKAT.class) stops working, and I start getting the
error:

testGetUser(se.su.it.sukat.test.AccountFacadeImplTest) Time elapsed:
0.04 sec <<< FAILURE!
Wanted but not invoked se.su.it.sukat.SUKAT.newInstance(
"sukat.properties"
);
Actually, there were zero interactions with this mock.
...

I know for a fact that the method SUKAT.newInstance is called when I
get this error.

I tried breaking AccountFacadeImplTest into multiple classes, with
some success. The first class contained 13 tests that all passed, but
the second class started behaving weird after only 4 tests. The only
workaround to this is to put the prepareForTest annotation at method
level, but then I eventually trigger OutOfMemory.errors;
http://code.google.com/p/powermock/issues/detail?id=207&q=prepareForTest.

I havent been able to figure out why this happens, since there's no
actual pattern to when I start getting failures I shouldn't be
getting. I'm hoping someone can help me figure this out.
> ...
>
> läs mer »

Johan Haleby

unread,
Nov 19, 2010, 3:47:38 AM11/19/10
to powe...@googlegroups.com
Hi, 

Sounds very strange indeed. Could you please create a small example that demonstrates the issue you're having otherwise it's extremely hard to verify :/

/Johan

Alex

unread,
Nov 19, 2010, 9:33:50 AM11/19/10
to PowerMock
Ok, I've made a small example. But I figured out that the problem is
caused because of static fields which I think is not the fault of
PowerMock.


public class MyClass1 {

private static final MyClass2 class2Obj = new MyClass2();

public MyClass1() {
}

public MyClass2 getClass2Obj() {
return class2Obj;
}

}


public class MyClass2 {

}

import static org.junit.Assert.*;
import static org.powermock.api.mockito.PowerMockito.*;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;


@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass1.class)
public class PowerMockTest {

private MyClass1 myClass1Obj;

private MyClass2 myMockObj;

@Before
public void setUp() throws Exception {
myMockObj = mock(MyClass2.class);
whenNew(MyClass2.class).withNoArguments().thenReturn(myMockObj);

myClass1Obj = new MyClass1();
}

@Test
public void test1() {
assertSame(myMockObj, myClass1Obj.getClass2Obj());
}

@Test
public void test2() {
assertSame(myMockObj, myClass1Obj.getClass2Obj());
}

}

Of course, the constructors for the static fields are called just once
when the class is loaded and well I don't think there's any way for
PowerMock to deal with that. I don't think it's PowerMock's
responsibility either. I mean when it comes to testing you always have
this problem.



On 19 Nov., 09:47, Johan Haleby <johan.hal...@gmail.com> wrote:
> Hi,
>
> Sounds very strange indeed. Could you please create a small example that
> demonstrates the issue you're having otherwise it's extremely hard to verify
> :/
>
> /Johan
>
> ...
>
> Erfahren Sie mehr »

Johan Haleby

unread,
Nov 19, 2010, 10:00:42 AM11/19/10
to powe...@googlegroups.com
Thanks for the examples. Do you mean that you no longer have the problem?! Do you expect your test example to fail or pass if everything is working correctly?

/Johan


--

ilinca

unread,
Nov 19, 2010, 10:17:23 AM11/19/10
to PowerMock
I'm afraid I haven't been able to reliably reproduce it. When testing
it on actual code, stuff seems to break when using Mockito extension
or Mockito on it's own, but again, I can't guarantee that it's always
so, because it didn't occur in my simple sample that I tried to
provide you with.

Here's an example where it seems to occur fairly often:

public void testGetAccountDomain() throws Exception{
mockStatic(SUKAT.class);
when(SUKAT.newInstance(SUKAT_REF)).thenReturn(sukat);
mockStatic(SearchResultUtils.class);

Attributes attributes = new BasicAttributes();
//put some attributes
Attribute oc = new BasicAttribute("objectClass");
//add some objectclass
attributes.put(oc);
SearchResult entry = new SearchResult("somename", new Object(),
attributes);

when(sukat.findUserByUid("myUid")).thenReturn(entry);
String domain = accountFacade.getAccountDomain("myUid");

//HERES WHERE STUFF BREAKS - Uncommenting these two lines, breaks
the upcoming test.
//PowerMockito.verifyStatic(Mockito.never());
//SearchResultUtils.domainOf(entry);
assertEquals("somedomain", domain);
}

@Test
//@PrepareForTest(SUKAT.class)
public void testGetAccountDomainInvalidUser() throws Exception{
mockStatic(SUKAT.class);
when(SUKAT.newInstance(SUKAT_REF)).thenReturn(sukat);

when(sukat.findUserByUid("myUid")).thenReturn(null);
try{
accountFacade.getAccountDomain("myUid");
fail("should throw error when no user is found");
} catch (Exception e){
assertTrue(true);
}

In most cases it seems to be related to PowerMock.verifyStatic() or
Mockito.verify(mock).doSomething() calls, allthough like above, it's
not always the tests containing those calls that actually breaks, but
tests that are executed afterwards. I wish I could give you a better
example, but the attempts I've made to reproduce this error in simple
tests have failed. I don't know if this has anything to do, with the
fact that our "real" tests run in a fairly complicated environment
with spring dependencies and whatnot.

Since this is so hard to reproduce I guess I'll have to wait for the
fix for the memory leak issues to be able to properly test this
product. Adding PrepareForTest-annotation on method level is the only
fix I've found for this problem so far.

On 19 Nov, 09:47, Johan Haleby <johan.hal...@gmail.com> wrote:
> Hi,
>
> Sounds very strange indeed. Could you please create a small example that
> demonstrates the issue you're having otherwise it's extremely hard to verify
> :/
>
> /Johan
>
> ...
>
> läs mer »

Alex

unread,
Nov 19, 2010, 10:29:50 AM11/19/10
to PowerMock
Well, I have the problem still ;-) If everything is working correctly
I expect the test to pass but it does not. However, I think the
problem lies on my side by using the static field and cannot be
handled properly by PowerMock.

On 19 Nov., 16:00, Johan Haleby <johan.hal...@gmail.com> wrote:
> Thanks for the examples. Do you mean that you no longer have the problem?!
> Do you expect your test example to fail or pass if everything is working
> correctly?
>
> /Johan
>
> ...
>
> Erfahren Sie mehr »

Johan Haleby

unread,
Nov 19, 2010, 10:58:50 AM11/19/10
to powe...@googlegroups.com
I haven't executed the test yet but I think I understand what you're after. I actually think that it's correct that your example should fail :) The reason is that the static field in MyClass1 is initialized before your setup method is even called. This is the way Java works, it initializes static fields and constructors before instance fields and constructors. What is it that you really want to do? I assume you want to change the value of the static field in MyClass1? If you don't care that the field is initialized once then you could simply do:

@Before
public void setUp() throws Exception {
               myMockObj = mock(MyClass2.class);
               Whitebox.setInternalState(MyClass1.class, myMockObj);

               myClass1Obj = new MyClass1();
       }
}

If you want to prevent MyClass2 from being initialized before you set the mock you need to suppress that static initializer of MyClass1 by adding the SuppressStaticInitializerFor annotation:
@RunWith(PowerMockRunner.class)
@SuppressStaticInitializerFor("org.packagename.MyClass1")
@PrepareForTest(MyClass1.class) // You can remove this annotation if you like because SuppressStaticInitializerFor automatically prepares it as well
public class PowerMockTest {
...

@Before
public void setUp() throws Exception {
      myMockObj = mock(MyClass2.class);
      Whitebox.setInternalState(MyClass1.class, myMockObj);

      myClass1Obj = new MyClass1();
      }
}

...
}

Keep in mind that ALL static fields will be null in MyClass1 if you suppress its static initializer.
/Johan

Alex

unread,
Nov 19, 2010, 2:08:57 PM11/19/10
to PowerMock
You are exactly right. The solution using Whitebox is exactly what I
needed so mission accomplished there :-). I didn't use it in the first
place because I tought it's only for non-static fields. By the way: My
@PrepareForTest annotations get pretty long with all the qualified
names, isn't it possible to give the annotation an array of classes
instead of qualified class names?

Regards, Alex

On 19 Nov., 16:58, Johan Haleby <johan.hal...@gmail.com> wrote:
> I haven't executed the test yet but I think I understand what you're after.
> I actually think that it's correct that your example *should* fail :) The
> reason is that the static field in MyClass1 is initialized *before* your
> On Fri, Nov 19, 2010 at 4:29 PM, Alex <alexander.weickm...@googlemail.com>
> ...
>
> Erfahren Sie mehr »

Johan Haleby

unread,
Nov 19, 2010, 5:52:53 PM11/19/10
to powe...@googlegroups.com
Hmm you can do @PrepareForTest({MyClass1.class, MyClass2.class, ..}) to
prepare multiple classes or you can use the fullyQualifiedNames version
and pass in e.g. "org.mypackage.*". But if you have that many classes to
prepare you should probably think again and refactor your code instead
of writing complex tests.

You can't use classes in the "SuppressStaticInitializerFor" because then
the class would load before it's passed to PowerMock which prevents us
from suppressing the initializer.

/Johan

>> Erfahren Sie mehr �

Alex

unread,
Nov 23, 2010, 7:01:55 PM11/23/10
to PowerMock
Well the main reason why I often need to prepare classes is because I
follow "design for inheritance or else prohibit it" which means I have
a lot of final classes. If they are internal, e.g. package-private
then I don't create interfaces for them -> PrepareForTest ;-)
> ...
>
> Erfahren Sie mehr »
Reply all
Reply to author
Forward
0 new messages