How to mock EntityManager

谁都会走 提交于 2021-02-10 20:15:51

问题


I first posted a question on here about a Unit test, EntityManager and NullPointerException. And then somebody advised me to look up mocking, so I did. However, I am still facing the same issues. I like to know if I am missing something? Is there a setup I forgot to add? Do I require to indicate the location of the persistence XML file? I would appreciate any help with that.

I looked at other similar questions. However, I didn’t have much luck.

1./ How to mock object in EntityManager?

2./ How to mock EntityManager?

3./ Mocking EntityManager

package com.beetlehand.model.dao;

import com.beetlehand.model.AttributeEntity;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

public class AttributeDaoTest {

    public AttributeEntity entity = new AttributeEntity();
    
    private AutoCloseable closeable;

    @Mock
    private EntityManager entityManager;

    @Test
    void testGetById(){

        TypedQuery<AttributeEntity> queryByMock =  (TypedQuery<AttributeEntity>) Mockito.mock(TypedQuery.class);
        Mockito.when(entityManager.createQuery("SELECT a FROM attributes a WHERE attribute_id = 1"))
                .thenReturn(queryByMock);
        Mockito.when(queryByMock.getSingleResult()).thenReturn(entity);

    }

    @BeforeEach
    void setUp() {

        closeable = MockitoAnnotations.openMocks(this);

    }

    @AfterEach
    void tearDown() throws Exception{

        closeable.close();

    }

}

Persistence configuration file,

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">

    <persistence-unit name="NewPersistenceUnit">
        <class>com.beetlehand.model.AttributeEntity</class>
        <class>com.beetlehand.model.AttributeValueEntity</class>
        <class>com.beetlehand.model.AuditEntity</class>
        <class>com.beetlehand.model.CategoryEntity</class>
        <class>com.beetlehand.model.CustomerEntity</class>
        <class>com.beetlehand.model.DepartmentEntity</class>
        <class>com.beetlehand.model.OrderDetailEntity</class>
        <class>com.beetlehand.model.OrdersEntity</class>
        <class>com.beetlehand.model.ProductEntity</class>
        <class>com.beetlehand.model.ProductAttributeEntity</class>
        <class>com.beetlehand.model.ProductCategoryEntity</class>
        <class>com.beetlehand.model.ReviewEntity</class>
        <class>com.beetlehand.model.ShippingEntity</class>
        <class>com.beetlehand.model.ShippingRegionEntity</class>
        <class>com.beetlehand.model.ShoppingCartEntity</class>
        <class>com.beetlehand.model.TaxEntity</class>
        <properties>
            <property name="toplink.jdbc.url" value="jdbc:mysql://localhost:3306/beetlehand"/>
            <property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/beetlehand"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="openjpa.ConnectionURL" value="jdbc:mysql://localhost:3306/beetlehand"/>
            <property name="openjpa.ConnectionDriverName" value="com.mysql.jdbc.Driver"/>
            <property name="eclipselink.jdbc.url" value="jdbc:mysql://localhost:3306/beetlehand"/>
            <property name="eclipselink.jdbc.driver" value="com.mysql.jdbc.Driver"/>

            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/beetlehand"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="toor" />

        </properties>
    </persistence-unit>
</persistence>

回答1:


That "someone" could have been me so I try to clarify my perhaps unclear comment.

As the comment suggests you might not want to mock EntityManager. It is possible I think but there is no point to do that. The issue now is more likely that you need to clarify yourself about what is unit and what integration test and when to do which.

I try to clarify this a bit.

You have a query:

SELECT a FROM attributes a WHERE attribute_id = 1;

How do you test that query is well formed and it returns what you want? You might open db shell and run some queries to see it works but to have it in Java and as regression test there is no other way than create an integration test that inserts some test data to db to which you then apply this query.

So no mocking of EntityManager because you need the real one. That requires test environment/framework that is capable to inject this real EntityManager (I used to use Arquillian some five years ago but guess there are more than that).

You might have designed your DAO wisely and have a class like:

@LocalBean // not necessarily a local bean but something that declares it as a bean
public class MyDao {
    
    @Resource
    private EntityManager em;

    public AttributeEntity getByAttributeId(Long id) {
        // return the AttributEntity found by the query you now have
    }
}

Then you would have integration test that looks something like:

public class MyDaoIT {
    
    @Resource 
    // EntityManager is in your MyDao and you should no more be interested 
    // about it in your test
    private MyDao myDao;

    public AttributeEntity testGetByAttributeId1() {
        // just as an example
        assertEquals(1L, myDao().getByAttributeId(1).getAttributeId());
    }
}

The above -again- needs some test framework which initializes a test container.

But what does this have to do with mocking anyway? Mocking can be used used also with integration tests if there is no point to make "real" initialization for some object but EntityManager is not the case.

You could say - as some kind a rule of thumb - that mocking is applied to stuff you do not want to initialize and/or test but your code needs it. And Usually this is the case with unit tests where you do not want to wait 20 seconds to get test container initialize to run 1ms test and make redundant tests agains a real database, for example.

You also could have some service class that uses MyDao and which has some business logic you want to test:

public class MyService {
    @Resource
    private MyDao myDao;

    // just some very bad example method
    public AttributeEntity getCopyOfAttributeEntity(Long id) {
        AttributeEntity ae = myDao.getByAttributeId(id);
        AttributeEntity aeCopy = ae.clone(); // suppose clone works ;)
        aeCopy.setAttributeId(ae.getAttributeId() + 1);
    }
}

What do you want to test here? Not the query since you already tested it in your integration test, right? Here you want to mock your MyDao and test the business logic in your service. So resulting test class like:

public class MyServiceTest {
    @Mock
    private MyDao myDao;
    @InjectMocks
    private MyService myService;

    public AttributeEntity testGetCopyOfAttributeEntity1() {
        // The part where mock stuff is initialized
        AttributeEntity ae = new AttributeEntity();
        ae.setAttributeId(1L);
        doReturn(ae).when(myDao).getByAttributeId(1L);
        // The part you test the logic in your service
        AttributeEntity aeCopy = myService.getCopyOfAttributeEntity(1L);
        assertEquals(2, aeCopy.getAttributeId());
    }
}


来源:https://stackoverflow.com/questions/64413165/how-to-mock-entitymanager

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!