简体   繁体   中英

Mock spring's LocalContainerEntityManagerFactoryBean method using Mockito?

I am trying to write unit testcases for the below code and am trying to mock the EntityManager implementation. I am unable to do so and I get null entity manager bean in my test class.

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

The test code I have written to mock it is below:

@InjectMocks
    private LocalContainerEntityManagerFactoryBean emMock = new LocalContainerEntityManagerFactoryBean();

...

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

I should return a list when this is called as output So i need the whole method to be mocked. Please help !

First off all instead of @InjectMocks you should be using @Mock and put the @InjectMocks on the class you are trying to unit test.

However the fact that you are even considering mocking the LocalContainterEntityManagerFactoryBean is a sign that your code is flawed. You shouldn't be using the LCEMFB in code. It is only for configuration. It is a FactoryBean that creates an EntityManagerFactory so actually you should be injecting an EntityManagerFactory into your code which you should be mocking.

Instead of wiring the LCEMFB use the plain EMF and get an instance by annotating the field with @PersistenceUnit .

@PersistenceUnit
private EntityManagerFactory emf;

Then your method is also a bit cleaner

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

However what you actually should be doing is injecting an EntityManager and don't try to create one yourself (your code is still flawed as you aren't closing the transaction nor the created EntityManager which in turn will eventually lead you to being unable to connect to your database as the underlying Connection remains open as well.

So instead of injecting either the LCEMFB or a EMF use a plain EntityManager instead and let spring manage it for you. To have spring manage the transaction make sure there is an @EnableTransactionManagement or <tx:annotation-driven /> in your configuration else it won't work.

@PersistenceContext
private EntityManager em;

Now your method is really focussed on what it should do, get data from the database.

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

Now in your test you should only need to mock the EntityManager .

All of this is also explained in the ORM chapter of the Spring Reference guide.

Another thing that worries me is that you are using a String and parsing that to be used as a query. This is potentially dangerous and a cause for SQL injection attacks . Instead of doing the formatting yourself you should let it be handled by Hibernate or JDBC.

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

The code above assumes a query in the form of SELECT * FROM YOURTABLE WHERE siteId=:siteId and startTime >= :startTime and endTime <= :endTime (or whatever your SQL looks like).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM