【问题标题】:Cannot test JPA + Spring无法测试 JPA + Spring
【发布时间】:2012-04-04 12:21:56
【问题描述】:

我有以下测试用例:

@ContextConfiguration("/spring/test-context.xml")
@TransactionConfiguration(transactionManager="txManager")
@Transactional()
public class MyEntityDaoTestCase extends AbstractJUnit4SpringContextTests { 

    @Autowired
    private MyEntityDao dao;

    @Test
    public void testSave_success() {
        MyEntity e = new MyEntity();
        dao.save(e);
        MyEntity result = dao.findById(e.getId());
        assertNotNull(result);      
    }
}

我的 DAO 定义如下:

public abstract class MyEntityDAO {

    @PersistenceContext
    private EntityManager mEntityManager;

    public void save(MyEntity entity) {
        mEntityManager.persist(entity);
    }

    public MyEntity findById(Long id) {
        return mEntityManager.find(mEntityClass, id);
    }
}

我的 Spring 配置如下:

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/tx 
           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <!-- 
        Bean post-processor for JPA annotations 
    -->
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

    <!--
        JPA entity manager factory 
     -->
    <bean id="jpaEntityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
      <property name="persistenceUnitName" value="unit-test-pu"/>
    </bean>

    <!--
        Transaction manager 
     -->
    <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="jpaEntityManagerFactory"/>
    </bean>

    <!-- 
        Enable the configuration of transactional behavior based on annotations 
    -->
    <tx:annotation-driven transaction-manager="txManager"/>

    <!--
        DAO instance beans 
     -->
    <bean id="mockEntityDao" class="mypackage.MyEntityDao"></bean>

</beans>

我在执行测试时没有收到任何错误,但它不会通过。看起来 findById() 方法不会在数据库中找到实体。谁能建议如何正确测试这种情况?

编辑:

我的 JPA 提供程序处于休眠状态。我正在使用内存中的 HSQLDB 进行单元测试,并具有以下配置:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="unit-test-pu" transaction-type="RESOURCE_LOCAL">   
      <properties>
         <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver"/>
         <property name="javax.persistence.jdbc.user" value="sa"/>
         <property name="javax.persistence.jdbc.password" value=""/>
         <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:."/>
         <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
         <property name="hibernate.archive.autodetection" value="class"/>
         <property name="hibernate.show_sql" value="true"/>
         <property name="hibernate.format_sql" value="true"/>
         <property name="hibernate.hbm2ddl.auto" value="create"/>
      </properties>      
   </persistence-unit>
</persistence>

【问题讨论】:

  • 什么是 JPA 提供者和配置?
  • 我正在使用休眠。我已经在我的帖子中添加了我的配置。
  • MyEntity 已正确持久化,所以 e.getId() 的 id 不为空?
  • MyEntity 的 id 为空。但是控制台中没有例外。
  • 尝试在没有 Transaction 注释的情况下运行测试,如果 id 仍然为空,则存在持久化 MyEntity 而不是事务的问题。

标签: java spring jpa transactions


【解决方案1】:

您可以尝试使用 @TransactionalConfiguration 注释和 Spring JUnit 运行器。

比如把你的班级改成这样:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring/test-context.xml")
@TransactionConfiguration(transactionManager="txManager", defaultRollback=true)
@Transactional
public class MyEntityDaoTestCase {

这也意味着您不需要扩展抽象案例(因为您使用的是 Spring 运行器)——除非您特别喜欢这种方法。

这里有更多details

【讨论】:

  • 我已尝试设置 defaultRollback = true。它不起作用。
  • 只是一个评论:请不要将defaultRollback设置为true! Hibernate 将插入/更新语句保留在内存中,直到它们被刷新到数据库(通过选择或显式刷新)。因此,Hibernate 可能永远不会在您的测试中接触数据库,这意味着您在部署应用程序时仍然会出现错误尽管您进行了测试
  • 这正是我的问题。我想让休眠状态刷新以测试我的情况。看看我的测试方法,你就会明白我的意思......
【解决方案2】:

如果您严格遵循 TDD,则不应使用内存数据库,而应模拟所有内容。问题是persist 方法返回void。因此,您无法测试正确的响应(具有由数据库生成的 id 的实体)解决此问题的一种方法是向我们提供 Mockito doAnswer 方法,这里是一个示例:

@RunWith(MockitoJUnitRunner.class)
public class CookieRepositoryTest {

@Mock
 EntityManager em;

@Mock
 TimeService timeService;

@InjectMocks
 CookieRepository underTest = new CookieRepository();

@Test
 public void testCreateEntity() throws Exception {
 Cookie newCookie = new Cookie();

when(timeService.getTime()).thenReturn(new DateTime(DateTimeZone.UTC));

doAnswer(new Answer<Brand>() {
 @Override
 public Brand answer(InvocationOnMock invocationOnMock) throws Throwable {
 Object[] args = invocationOnMock.getArguments();
 Cookie cookie = (Cookie) args[0];
 cookie.setId(1);
 return null;
 }

}).when(em).persist(any(Brand.class));

Cookie persistedCookie = underTest.createEntity(newCookie);
 assertNotNull(persistedCookie.getId());
 }

}

完整的解释可以在我的博客post找到。

【讨论】:

    【解决方案3】:

    我认为您应该扩展 AbstractTransactionalJUnit4SpringContextTests 而不是 AbstractJUnit4SpringContextTests 以使 @Transactional-annotations 产生任何效果。

    【讨论】:

      【解决方案4】:

      如果您想测试持久层,还可以查看 DBUnit 功能。

      您可以在此处找到来自 Petri Kainulainen 的一篇关于在基于 Spring 的场景中测试持久层(在本例中为 JPA)的好文章:

      http://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-integration-testing/

      这样,您可以测试 DAO 类的行为是否符合预期,在数据库中写入和读取,在服务层上您可以避免测试这方面,更多地关注业务逻辑的“模拟”方法。

      希望对你有帮助

      弗兰

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-06-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-16
        • 2018-06-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多