使用 PowerMock 防止静态初始化

Use PowerMock to prevent static initialization

所以我的 class 测试中有一个静态变量。我试图用 Powermockito 模拟它,但出现错误。

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

我的测试class是这样的:

@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);

  }

}

错误是

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"

你能告诉我哪里错了吗? 我只想测试 methodUnderTest() 有什么方法可以防止 EntityManager em?

的静态初始化

通过实践更好的设计原则。

Can you tell me where I am going wrong?

与该静态依赖项的紧密耦合现在使您的代码难以测试。

使它成为通过构造函数注入的显式依赖项。

public class ClassUnderTest{
    private EntityManager em;

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

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

现在在测试时您可以简单地传递一个 null EntityManager,因为测试不需要它。

使用 PowerMockito.when 而不是 Mockito.when

并删除 ClassUnderTestmockStatic

所以:

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

这段代码对我有用

@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);
  }

}

一些要点

  1. 因为 EntityManager 不是 @Aurowired,所以不需要 @InjectMocks
  2. 既然要调用ClassUnderTest::methodUnderTest下的代码,就不要在@PrepareForTest下使用ClassUnderTest
  3. 不要PowerMockito.mockStatic(ClassUnderTest.class);

说了这么多。你应该认真考虑重构你的代码以最小化(如果可能的话消除)所有静态方法和字段。

正如许多其他已经提到的,你真的不应该使用静态依赖,因为它会导致那种可怕的可测试设计。

但是,如果您无法更改被测代码,您可以通过反射注入 EntityManager:

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 "";
    }
}

这样您还可以关闭潘多拉魔盒又名 PowerMock 并继续进行基本的单元测试和模拟。