0

I am using Junit 4.12 with PowerMock 1.6 with Mockito. I have also used PowerMockRule library as described here. I am trying to execute initialization code for all of my test cases exactly once as described in this SO Thread. Its executing the initialization code exactly one time however, if I do ServiceInitializer.INSTANCE inside test method it returns me new object. I am not able to understand this behavior. Does anyone have any idea why this is happening? If I execute my code without PowerMockRule Library and run my test with PowerMockRunner then it works fine but in that case my ClassRule is not getting executed.

    public class ServiceInitializer extends ExternalResource {
      public static final TestRule INSTANCE = new ServiceInitializer();
      private final AtomicBoolean started = new AtomicBoolean();

      @Override protected void before() throws Throwable {
        if (!started.compareAndSet(false, true)) {
          return;
        }
        // Initialization code goes here
        System.out.println("ServiceInitializationHelper:"+this); //Print Address @3702c2f1
      }

      @Override protected void after() {
      }
    }




    class BaseTest{
            @Rule
            public PowerMockRule powerMockRule = new PowerMockRule();

              @ClassRule
              public static final TestRule serviceInitializer = ServiceInitializer.INSTANCE;

              @Before
              public final void preTest() {
                // some code
              }

              @After
              public final void postTest() {
                //some code
              }
    }


@PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
      @Test
      public void testMethodA_1(){
            System.out.println(ServiceInitializer.INSTANCE);//Print Address @54d41c2b
      }
}

Update

I printed the classloader for the classes and it turns out for first print statement the classloder was sun.misc.Launcher$AppClassLoader and for the second print statement the classloder was org.powermock.core.classloader.MockClassLoader. How can I solve this?

Community
  • 1
  • 1
Prerak Tiwari
  • 3,436
  • 4
  • 34
  • 64

3 Answers3

3

You don't have a singleton. You have a static INSTANCE variable. Keep in mind that one of those can exist for every classloader you have.

Instead make an enum of ServiceInitializer, like so

public enum ServiceInitializer {
  INSTANCE;

  // rest of class goes here
}

And rely on the JVM's language contracts to ensure the singleton.

Or, better yet, write your code to handle situations where more than one ServiceInitializer can exist, but it just happens that your program only uses one instance. This is the ideal choice, allowing you to alternate between the real ServiceInitializer and a mock if desired.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
1

Edwin is correct; this is an issue with PowerMock creating a new ClassLoader for every test. I strongly recommend refactoring your code so it can be tested without PoeerMock and switch to Mockito.

These books may be helpful

In the mean time, you can reference ServiceInitializer from your base class:

    public class ServiceInitializer extends ExternalResource {
      public static final ServiceInitializer INSTANCE = new ServiceInitializer();
      private final AtomicBoolean started = new AtomicBoolean();

      @Override protected void before() throws Throwable {
        if (!started.compareAndSet(false, true)) {
          return;
        }
        // Initialization code goes here
        System.out.println("ServiceInitializationHelper:"+this);
      }

      @Override protected void after() {
      }
    }




    class BaseTest{
            @Rule
            public PowerMockRule powerMockRule = new PowerMockRule();

              @ClassRule
              public static final ServiceInitializer serviceInitializer = ServiceInitializer.INSTANCE;

              @Before
              public final void preTest() {
                // some code
              }

              @After
              public final void postTest() {
                //some code
              }
    }


@PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
      @Test
      public void testMethodA_1(){
            System.out.println(serviceInitializer);
      }
}
NamshubWriter
  • 23,549
  • 2
  • 41
  • 59
  • Unfortunately, refactoring is not an option to me and I can not simply switch to Mockito since I have lots of static method in the code which needs to be tested. – Prerak Tiwari Jan 05 '16 at 16:32
  • @PrerakTiwari If refactoring is not an option, then IMHO your project will become harder and harder to maintain. Find a way to refactor; the books I suggested (especially Working Effectively With Legacy Code) will help. – NamshubWriter Jan 05 '16 at 16:45
0

Well I finally found the work around for this problem. As explained in my question my class was getting loaded by two different class loaders and thus causing problems for me. In order to resolve my issue I used @PowerMockIgnore annotation in order to defer its loading as follows:

@PowerMockIgnore({"com.mypackage.*"})
class BaseTest{
      // Stuff goes here
}

This annotation tells PowerMock to defer the loading of classes with the names supplied to value() to the system classloader. You can read about this annotation from here.

Prerak Tiwari
  • 3,436
  • 4
  • 34
  • 64