首页 > 解决方案 > JUnit +泛型抽象类测试库:我的测试中总是出现nullpointerexception,抽象方法中没有进行字段初始化

问题描述

在 Jave EE 应用程序中,我想为内存数据库集成测试创建一个基础测试,以初始化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

模拟:org.mockito:mockito-core:1.10.19 JUnit:junit:junit:4.11

我没有使用 Spring/Spring Boot。

标签: javagenericsjunitsuperclass

解决方案


为什么我的 serviceBean 没有初始化?

因为你已经setup()在子类中被覆盖了。

要么给子类(或超类)setup()一个不同的名字,要么从子类中调用超类。


推荐阅读