JUnit + generic abstract class 测试基础:在我的测试中总是出现空指针异常,字段初始化没有在抽象方法中完成
JUnit + generic abstract class test base: always got nullpointerexception in my test, field initialization is not done in abstract method
在 Jave EE 应用程序中,我想为 in-memory 数据库集成测试创建一个基础测试,以初始化 EntityManagerFactory
和 EntityManager
。另外,要测试的服务 bean 未确定,所以我将其设为通用。
我有:
package xxxxx;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import static org.mockito.Mockito.mockingDetails;
import static org.mockito.Mockito.spy;
/**
* T: the service bean class to test
*/
public abstract class H2DBIntTestBase<T> {
protected EntityManager realEntityManager;
protected static EntityManagerFactory factory;
protected T serviceBean;
public abstract void setServiceBean();
public abstract String getPUName();
@Before
public void setup() {
// as we cannot statically get class name in @BeforeClass of super class, we have to do
// it like this. We don't care repetitive creation, as they are the same one, so we don't
// guard it with sychronization/double check.
if (factory == null) {
factory = Persistence.createEntityManagerFactory(getPUName());
}
realEntityManager = factory.createEntityManager();
EntityManager spy = spy(realEntityManager);
setServiceBean();
try {
// inject the real entity manager, instead of using mocks
Field entityManagerField = serviceBean.getClass().getDeclaredField("entityManager");
entityManagerField.setAccessible(true);
entityManagerField.set(serviceBean, spy);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError("should not reach here");
}
}
@After
public void teardown() {
realEntityManager.close();
}
}
并且,在我的具体 int 测试中:
@RunWith(MockitoJUnitRunner.class)
public class MyServiceBeanIntTest extends H2DBIntTestBase<MyServiceBean> {
private Query<String> inputQuery;
@Override
public void setServiceBean() {
super.serviceBean = new SubscriptionHistoryPersistenceServiceBean();
}
@Override
public String getPUName() {
return serviceBean.getClass().getName();
}
@Before
public void setup() {
inputQuery = new Query<>();
... // other stubbings
}
@Test
public void test1() {
try {
// when
List<SubscriptionHistory> actual = serviceBean.doSth(inputQuery, Enum.TypeA);
// <----------- here serviceBean is always null, why?
// then
// assertions
} catch (DataLookupException e) {
throw new AssertionError("should not reach here");
}
}
}
但是,我的考试总是有NPE,一开始是@After
,然后是我的@Test
。
为什么我的serviceBean
没有初始化?超级测试的@BeforeClass
和@Before
不应该是运行BEFOREchild类吗?
Mockito:org.mockito:mockito-core:1.10.19
JUnit:junit:junit:4.11
我没有使用 Spring/Spring 启动。
Why my serviceBean is not initialized?
因为你在子类中覆盖了setup()
。
要么给子类(或超类)setup()
一个不同的名字,要么从子类中调用超类。
在 Jave EE 应用程序中,我想为 in-memory 数据库集成测试创建一个基础测试,以初始化 EntityManagerFactory
和 EntityManager
。另外,要测试的服务 bean 未确定,所以我将其设为通用。
我有:
package xxxxx;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import static org.mockito.Mockito.mockingDetails;
import static org.mockito.Mockito.spy;
/**
* T: the service bean class to test
*/
public abstract class H2DBIntTestBase<T> {
protected EntityManager realEntityManager;
protected static EntityManagerFactory factory;
protected T serviceBean;
public abstract void setServiceBean();
public abstract String getPUName();
@Before
public void setup() {
// as we cannot statically get class name in @BeforeClass of super class, we have to do
// it like this. We don't care repetitive creation, as they are the same one, so we don't
// guard it with sychronization/double check.
if (factory == null) {
factory = Persistence.createEntityManagerFactory(getPUName());
}
realEntityManager = factory.createEntityManager();
EntityManager spy = spy(realEntityManager);
setServiceBean();
try {
// inject the real entity manager, instead of using mocks
Field entityManagerField = serviceBean.getClass().getDeclaredField("entityManager");
entityManagerField.setAccessible(true);
entityManagerField.set(serviceBean, spy);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError("should not reach here");
}
}
@After
public void teardown() {
realEntityManager.close();
}
}
并且,在我的具体 int 测试中:
@RunWith(MockitoJUnitRunner.class)
public class MyServiceBeanIntTest extends H2DBIntTestBase<MyServiceBean> {
private Query<String> inputQuery;
@Override
public void setServiceBean() {
super.serviceBean = new SubscriptionHistoryPersistenceServiceBean();
}
@Override
public String getPUName() {
return serviceBean.getClass().getName();
}
@Before
public void setup() {
inputQuery = new Query<>();
... // other stubbings
}
@Test
public void test1() {
try {
// when
List<SubscriptionHistory> actual = serviceBean.doSth(inputQuery, Enum.TypeA);
// <----------- here serviceBean is always null, why?
// then
// assertions
} catch (DataLookupException e) {
throw new AssertionError("should not reach here");
}
}
}
但是,我的考试总是有NPE,一开始是@After
,然后是我的@Test
。
为什么我的serviceBean
没有初始化?超级测试的@BeforeClass
和@Before
不应该是运行BEFOREchild类吗?
Mockito:org.mockito:mockito-core:1.10.19
JUnit:junit:junit:4.11
我没有使用 Spring/Spring 启动。
Why my serviceBean is not initialized?
因为你在子类中覆盖了setup()
。
要么给子类(或超类)setup()
一个不同的名字,要么从子类中调用超类。