@BeforeClass method is called multiple times

754 views
Skip to first unread message

Peter Most

unread,
Jul 24, 2009, 8:07:32 AM7/24/09
to PowerMock
Hi everybody,

when I use the following sample code, then the beforeClass() method
will be called for *every* test method. Only if I remove the
@PrepareForTest annotation from every test method, then everything is
working as expected.
I can observe this behavior either when running it inside eclipse
('Run' > 'Run As' > 'JUnit Test') or if I use the main method ('Run' >
'Run As' > 'Java Application')

Is this a bug or an 'incomplete' documentation issue or did I miss
something obvious here?

Regards Peter

package powermock;

import java.util.*;

import org.junit.*;
import org.junit.runner.*;
import org.junit.runner.notification.*;
import org.powermock.core.classloader.annotations.*;
import org.powermock.modules.junit4.*;
import org.powermock.api.easymock.*;

enum EnumToMock
{
Unimportant
}

@RunWith( PowerMockRunner.class )
public class TestPowerMock
{
private static EnumToMock enumToMock;

@BeforeClass
public static void beforeClass()
{
System.out.println( "Test.beforeClass called" );
enumToMock = PowerMock.createMock( EnumToMock.class );
}


@Test
@PrepareForTest( EnumToMock.class )
public void firstEmptyTest()
{
}


@Test
@PrepareForTest( EnumToMock.class )
public void secondEmptyTest()
{
}


public static void main( String arguments[] )
{
Result result = JUnitCore.runClasses( TestPowerMock.class );
List< Failure > failures = result.getFailures();
for ( Failure failure : failures )
{
System.out.println( failure.getDescription() );
System.out.println( failure.getTrace() );
}
}

}

Johan Haleby

unread,
Jul 24, 2009, 8:44:21 AM7/24/09
to powe...@googlegroups.com
Hi,

You shouldn't place @PrepareForTest annotation at the method-level of
the test as you do in this case. Rather place it at the class-level
(just before or after the @RunWith annotation) and remove it from the
test-methods. That may be the problem because if you place the
@PrepareForTest annotation at the method-level a new test runner will be
created with a new classloader for every test method so it's possible
that the "beforeclass" method is executed many times. Please give that a
try and see if it works, would be interesting for us to know as well.

/Johan

Johan Haleby

unread,
Jul 24, 2009, 9:09:19 AM7/24/09
to powe...@googlegroups.com
Sorry I didn't read your message close enough. I guess it was just what I suspected, when you place the @PrepareForTest method at the method-level which causes a new junit runner to be created the @BeforeClass method is executed again. I'm not sure whether we should consider this a bug or not since this is currently how PowerMock behaves (i.e. create a new junit runner per @PrepareForTest annotated method). If we were not to execute the @BeforeClass then you may have some uninitialized state in your test.. Hmm I'll add it as an issue and we'll think about what to do about it (we're about the refactor this mechanism entirely for version 1.3).

Nevertheless, in your example you should definitely place the PrepareForTest annotation at the class-level of your test.

/Johan

Peter Most

unread,
Jul 24, 2009, 9:11:10 AM7/24/09
to PowerMock
Hi Johan,

> You shouldn't place @PrepareForTest annotation at the method-level of
> the test as you do in this case. Rather place it at the class-level
> (just before or after the @RunWith annotation) and remove it from the
> test-methods. That may be the problem because if you place the
> @PrepareForTest annotation at the method-level a new test runner will be
> created with a new classloader for every test method so it's possible
> that the "beforeclass" method is executed many times. Please give that a
> try and see if it works, would be interesting for us to know as well.
>

Thanks for the suggestion, but I already discovered and described
that ;-)
To quote myself:

> > Only if I remove the
> > @PrepareForTest annotation from every test method, then everything is
> > working as expected.

So it is working then ;-)
But the question remains: Is it a bug? If it is unavoidable because of
the design, then
it might be worth mentioning in the documentation or in some kind of
FAQ.

Regards Peter

Peter Most

unread,
Jul 24, 2009, 9:16:09 AM7/24/09
to PowerMock

On Jul 24, 3:09 pm, Johan Haleby <johan.hal...@gmail.com> wrote:
> Sorry I didn't read your message close enough. I guess it was just what I
> suspected, when you place the @PrepareForTest method at the method-level
> which causes a new junit runner to be created the @BeforeClass method is
> executed again. I'm not sure whether we should consider this a bug or not
> since this is currently how PowerMock behaves (i.e. create a new junit
> runner per @PrepareForTest annotated method). If we were not to execute the
> @BeforeClass then you may have some uninitialized state in your test.. Hmm
> I'll add it as an issue and we'll think about what to do about it (we're
> about the refactor this mechanism entirely for version 1.3).
>
> Nevertheless, in your example you should definitely place the PrepareForTest
> annotation at the class-level of your test.
>
Thanks and please disregard my answer to your other post (bad
timing ;-)

Regards Peter


> /Johan

Joe Kearney

unread,
Jul 24, 2009, 9:19:46 AM7/24/09
to powe...@googlegroups.com
It's certainly forseeable that there might be cases where you want to (power)mock a class only on one test case, so I'd argue that it should be legal to do so. Presumably then the runner could run the other tests in one classloader and the method-annotated one in a new classloader, or maybe it'd just fall back to classloader-per-test-method. Either way you'd see the @BeforeClass method invoked once per class, but recall that a "class" at runtime is loaded as a class/classloader pair. You'd see the printed output here more than once, but if you checked, the class objects would be different in each run, for example.

Thanks,
Joe

2009/7/24 Peter Most <Peter...@gmx.de>

Johan Haleby

unread,
Jul 24, 2009, 9:32:54 AM7/24/09
to powe...@googlegroups.com
Hi,

This is exactly how it works today (if I'm not mistaken). Say you have 3 methods (A, B and C) and you only annotate one of them (B for example) with @PrepareForTest then A & C will be executed by one classloader and B will be executed with it's own classloader and it's own junit runner instance which (apparently) triggers the @BeforeClass annotated method to be executed twice, once for "A & C" and once for B. Perhaps this is exactly how it should be, but of course it should be documented so that people understand why it happens. The reason why we haven't documented this at all is that we've had lengthy discussion whether we should allow to place the @PrepareForTest annotation at the method level at all since our code gets a lot more complicated (and you could create a multiple tests to accommodate for the same behavior). But as I mentioned in the previous mail we have plans to internally refactor our classloading code for version 1.3 to make it more maintainable for ourselves :). Thanks for your input, we're happy to receive suggestions or comments!

/Johan

Peter Most

unread,
Jul 24, 2009, 9:40:56 AM7/24/09
to PowerMock
At least the documentation for the @PrepareForTest annotation should
be adjusted, because it currently states:

* <p>
* This annotation can be placed at both test classes and individual
test
* methods. If placed on a class all test methods in this test class
will be
* handled by PowerMock (to allow for testability). To override this
behavior
* for a single method just place a <code>&#064;PrepareForTest</code>
annotation
* on the specific test method. This is useful in situations where for
example
* you'd like to modify class X in test method A but in test method B
you want X
* to be left intact. In situations like this you place a
* <code>&#064;PrepareForTest</code> on method B and exclude class X
from the
* {@link #value()} list.
* <p>

Regards Peter

Johan Haleby

unread,
Jul 24, 2009, 9:45:15 AM7/24/09
to powe...@googlegroups.com
Well that still applies but it would probably be good to describe a bit
how it works and what the consequences are.
Reply all
Reply to author
Forward
0 new messages