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

时间:2011-02-07 09:54:35

标签: spring jpa junit

通过使用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();
    }
  } 
}

我的首发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);
  }
}

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

此致

2 个答案:

答案 0 :(得分:6)

代理机制

你正在敲打JDK代理。

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

请参阅this previous answer of mine以供参考。

<强>解决方案:

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

<强>参考:


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

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


交易测试

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

答案 1 :(得分:5)

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

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

此外,您的DAO图层不应该是事务性的。交易通常只在服务层有意义。

编辑:

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