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 数据库集成测试创建一个基础测试,以初始化 EntityManagerFactoryEntityManager。另外,要测试的服务 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()一个不同的名字,要么从子类中调用超类。