JUnit,JPA和Spring:如何确保单元测试在完成时保持数据库清洁

use*_*087 2 junit spring jpa

我试图SpringJunit4ClassRunner通过使用@Transactional注释来测试我的DAO类,而不会在完成后留下数据.我的DAO类包含(剥离):

@Repository
public class IdsFunctionJpaController {

  @PersistenceContext
  EntityManager em;

  public void save(IdsFunction function) {
    if (function.getId() == 0) {
      create(function);
    } else {
      update(function);
    }
  }

  @Transactional
  private void create(IdsFunction idsFunction) {
    try {
      em.persist(idsFunction);
    }
    catch (Exception e) {
      System.out.println(e);
    } finally {
      em.close();
    }
  }

  @Transactional
  private void update(IdsFunction function) {
    try {
      em.merge(function);
    } finally {
      em.close();
    }
  } 
}
Run Code Online (Sandbox Code Playgroud)

我的首发JUnit测试用例是

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext.xml"} )
public class IdsFunctionJpaControllerTest {

  @Autowired
  IdsFunctionJpaController dao;

  @Test
  @Transactional
  public void addFunction() {
    IdsFunction function = new IdsFunction();
    function.setDescription("Test Function Description");
    dao.save(function);
    assertTrue(function.getId() != 0);
  }
}
Run Code Online (Sandbox Code Playgroud)

我在这里尝试做的只是测试实体已经创建,但是这个测试失败了.如果我删除@Transactional注释,则测试通过,但测试实体仍保留在数据库中.我究竟做错了什么?

问候

Sea*_*oyd 6

代理机制

你正在敲打JDK代理.

您的dao.save()方法是非事务性的,它尝试调用事务方法create()update().但事务性事件发生在类外的JDK代理中,而save方法已经在类中.

请参阅我之前的答案以供参考.

解决方案:

  • 使您的save()方法具有事务性
  • (好多了)不要让你的DAO交易.事务属于服务层,而不是DAO层.

参考:


更新:我被@TransactionalDao方法中存在令人困惑的注释所误导.你应该删除它们,它们什么也不做,让人迷惑.

正如您在上面发布的链接中所看到的那样,@Transactional注释只有在公共方法中出现时才会生效.这些方法将从Spring bean外部调用(因此,您不能在一个代理一个或多个代理的类中使用一个方法同类的方法).


交易测试

Spring提供了特殊的事务测试支持类,如9.3.5.4事务管理中所述.如果您让测试类扩展,则AbstractTransactionalJUnit4SpringContextTests在每次测试后都会获得自动事务回滚.在大多数情况下,这正是您所需要的.


Sto*_*ica 5

您需要刷新会话.通常这发生在交易结束时,这就是为什么你通常只需要在测试中担心它.

注入EntityManager到测试类中并em.flush()在保存后调用.

此外,您的DAO层不应该是事务性的.事务通常仅在服务层中有意义.

编辑:

事实上,你的DAO也是完全错误的,这个测试无法显示.这些事务注释将无效,因为它们是内部方法调用.你也应该永远不要关闭EntityManager自己 - 容器(Spring)会为你做这件事.另外,不要捕获泛型Exceptions,当你做的时候不要只记录并忽略它们.异常应该传播到应该正确处理它们的服务层.另外,不要打印到stdout,使用适当的日志框架.