Noob
Noob

Reputation: 27

Use PowerMock to prevent static initialization

So I have a static variable in my class under test. I have tried to mock it With Powermockito but I am getting error.

public class ClassUnderTest{
  private static EntityManager em = AppEntityManager.createEntityManager();
   public static String methodUnderTest(){
       // this method dosent use EntityManager em
   }
}

My test class is like this:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ AppEntityManager.class, ClassUnderTest.class })

public class ClassUnderTestTest {
 @Mock
 private EntityManager emMock;
 @InjectMocks
 private ClassUnderTest feMock;

 static ClassUnderTest fe = new ClassUnderTest();

 @Before
 public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
 }

 @Test
 public void test() {
     PowerMockito.mockStatic(ClassUnderTest.class);
    PowerMockito.mockStatic(AppEntityManager.class);
    Mockito.when(AppEntityManager.createEntityManager()).thenReturn(emMock);


        String s = ClassUnderTest.methodUnderTest(myParams);
        // assertEquals(prams[i][1], s);
        System.out.println(s);

  }

}

The error is

Feb 22, 2018 9:37:31 AM oracle.jdbc.driver.OracleDriver registerMBeans
SEVERE: Error while registering Oracle JDBC Diagnosability MBean.
java.lang.LinkageError: loader constraint violation: loader (instance of org/powermock/core/classloader/MockClassLoader) previously initiated loading for a different type with name "javax/management/MBeanServer"

Can you tell me where I am going wrong? I just want to test methodUnderTest() and so is there a way I can prevent that static initialization of EntityManager em?

Upvotes: 1

Views: 4505

Answers (4)

Markus Spöri
Markus Spöri

Reputation: 116

As many other already mentioned, you really shouldn´t use static dependency’s as it leads to that kind of horrible testable design.

However, if you cannot change the code under test, you could inject the EntityManager via reflections:

public class ClassUnderTestTest {

    private ClassUnderTest classUnderTest;

    @BeforeEach
    public void setUp() throws Exception {
        final Field entityManagerField;

        classUnderTest = new ClassUnderTest();

        //Use getDeclaredField(...) since field is private
        entityManagerField = classUnderTest.getClass()
                                           .getDeclaredField("em");
        //Set accessible since field is private
        entityManagerField.setAccessible(true);
        entityManagerField.set(classUnderTest, 
                               Mockito.mock(EntityManager.class));
    }

    @Test
    public void test() {
        String s = classUnderTest.methodUnderTest();
        // assertEquals(prams[i][1], s);
        System.out.println(s);
    }
}

class ClassUnderTest{
    private static EntityManager em;
    public String methodUnderTest(){
        // this method dosent use EntityManager em
        // but solution don´t care
        return "";
    }
}

That way you can also close Pandora´s Box aka PowerMock and continue with basic UnitTesting and mocking.

Upvotes: 0

pvpkiran
pvpkiran

Reputation: 27068

This code works for me

@RunWith(PowerMockRunner.class)
@PrepareForTest({ AppEntityManager.class}) 
public class ClassUnderTestTest {

  @Mock
  private EntityManager emMock;

  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void test() {
    PowerMockito.mockStatic(AppEntityManager.class);
    Mockito.when(AppEntityManager.createEntityManager()).thenReturn(emMock);

    String s = ClassUnderTest.methodUnderTest(myParams);
    // assertEquals(prams[i][1], s);
  }

}

Some points

  1. Since EntityManager is not @Aurowired, there is no need for @InjectMocks.
  2. Since you want the code under ClassUnderTest::methodUnderTest to be called, don't use ClassUnderTest in @PrepareForTest
  3. Don't do PowerMockito.mockStatic(ClassUnderTest.class);

Having said all of these. You should seriously consider refactoring your code to minimize(if possible eliminate) all static methods and fields.

Upvotes: 1

Tobb
Tobb

Reputation: 12215

Use PowerMockito.when instead of Mockito.when.

And remove mockStatic of ClassUnderTest.

So:

PowerMockito.mockStatic(AppEntityManager.class);
PowerMockito.when(AppEntityManager.createEntityManager()).thenReturn(emMock);

Upvotes: 0

Nkosi
Nkosi

Reputation: 247333

By practicing better design principles.

Can you tell me where I am going wrong?

Tightly coupling to that static dependency is now making your code difficult to test.

Make that an explicit dependency that is injected via constructor.

public class ClassUnderTest{
    private EntityManager em;

    public ClassUnderTest(EntityManager em) {
        this.em = em;
    }

    public String methodUnderTest(){
       // this method dosent use EntityManager em
   }
}

Now when testing you can simply pass a null EntityManager since it is not needed in the test.

Upvotes: 1

Related Questions