使用Mockito模拟spring的LocalContainerEntityManagerFactoryBean方法?

时间:2016-07-22 22:17:39

标签: java spring unit-testing mockito

我正在尝试为下面的代码编写单元测试用例,并且我正在尝试模拟EntityManager实现。我无法这样做,我在我的测试类中获得了null实体管理器bean。

public List<Object[]> getForecastResults(String query, String siteId, long startTime, long endTimestamp)
{   
    List<Object[]> result = null;
    EntityManager em = null;
    try {           
        query = String.format(query, startTime, endTimestamp, siteId);
        logger.debug(" Query : " + query);
        em = localContainerEntityManagerFactoryBean.nativeEntityManagerFactory.createEntityManager();
        EntityTransaction et = em.getTransaction();
        et.begin();
        result = (List<Object[]>) em.createNativeQuery(query).getResultList();
        //logger.debug("Results from the query : " + query + " are :" + Utility.toJsonString(result, true));
    } catch (Exception ex) {            
        ex.printStackTrace();
        logger.error("Error Occurred while fetching the data for the query : " + query);            
    }           
    return result;
}

我写的模拟测试代码如下:

@InjectMocks
    private LocalContainerEntityManagerFactoryBean emMock = new LocalContainerEntityManagerFactoryBean();

...

Mockito.when(localContainerEntityManagerFactoryBean.nativeEntityManagerFactory.createEntityManager()).thenReturn();

当我将其作为输出调用时,我应该返回一个列表所以我需要整个方法进行模拟。请帮忙!

1 个答案:

答案 0 :(得分:2)

首先关闭所有内容而不是@InjectMocks,您应该使用@Mock并将@InjectMocks放在您尝试进行单元测试的类上。

然而,您甚至考虑嘲笑LocalContainterEntityManagerFactoryBean这一事实表明您的代码存在缺陷。你不应该在代码中使用LCEMFB。它仅用于配置。这是一个FactoryBean创建一个EntityManagerFactory,所以实际上你应该在代码中注入EntityManagerFactory,你应该嘲笑它。

而不是连接LCEMFB,而是使用普通EMF并通过使用@PersistenceUnit注释字段来获取实例。

@PersistenceUnit
private EntityManagerFactory emf;

然后你的方法也更清洁了

public List<Object[]> getForecastResults(String query, String siteId, long startTime, long endTimestamp)
{   
    List<Object[]> result = null;
    EntityManager em = null;
    try {           
        query = String.format(query, startTime, endTimestamp, siteId);
        logger.debug(" Query : " + query);
        em = emf.createEntityManager();
        EntityTransaction et = em.getTransaction();
        et.begin();
        result = (List<Object[]>) em.createNativeQuery(query).getResultList();
        //logger.debug("Results from the query : " + query + " are :" + Utility.toJsonString(result, true));
    } catch (Exception ex) {            
        ex.printStackTrace();
        logger.error("Error Occurred while fetching the data for the query : " + query);            
    }           
    return result;
}

然而,您实际应该做的是注入EntityManager并且不要尝试自己创建一个(您的代码仍然存在缺陷,因为您还没有关闭事务,也没有创建{{} 1}}反过来最终会导致您无法连接到您的数据库,因为基础EntityManager也会保持打开状态。

因此,不是注入ConnectionLCEMFB而是使用普通EMF,而是让Spring为您管理。要让spring管理交易,请确保您的配置中有EntityManager@EnableTransactionManagement,否则它将无法正常运行。

<tx:annotation-driven />

现在你的方法真正关注它应该做什么,从数据库中获取数据。

@PersistenceContext
private EntityManager em;

现在,在您的测试中,您只需要模拟@Transactional(readOnly=true) public List<Object[]> getForecastResults(String query, String siteId, long startTime, long endTimestamp) { query = String.format(query, startTime, endTimestamp, siteId); return em.createNativeQuery(query).getResultList(); }

所有这些也在Spring参考指南的ORM chapter中进行了解释。

让我担心的另一件事是你正在使用String并解析它以用作查询。这有潜在危险,是SQL injection attacks的原因。您应该让它由Hibernate或JDBC处理,而不是自己进行格式化。

EntityManager

上面的代码假设@Transactional(readOnly=true) public List<Object[]> getForecastResults(String query, String siteId, long startTime, long endTimestamp) { query = String.format(query, startTime, endTimestamp, siteId); Query q = em.createNativeQuery(query); q.setParameter("siteId", siteId) .setParameter("startTime", startTime) .setParameter("endTime", endTimestamp); return q.getResultList(); } 形式的查询(或者您的SQL看起来像什么)。