使用spring事务和回滚进行Junit异常测试

时间:2011-12-23 18:58:27

标签: java spring exception-handling transactions junit

所以我有一个有趣的问题,我需要一些帮助。我知道在使用junit的事务中回滚的问题有很多,但我相信我的问题和略有不同。为了让人们更好地理解这个问题,让我从头开始。

我已为用户管理系统实施了 UserManagementService 及其各自的 DAO 。有一种称为CreateUser(User obj)的通用方法,用于创建唯一用户。现在,有一个约束集,电子邮件地址是唯一的,所以如果我们尝试使用已经使用过的电子邮件地址调用此方法,我们会抛出一个名为 UserManagementException 的自定义异常及其各自的错误消息。所有这一切都很好,但是我遇到的问题是单元测试。哦,在我忘记之前,让我提一下我正在使用的软件堆栈[ Java,spring,hibernate ]

我的单元测试类使用Transactional注释为每个实际命中db的方法注释。这些方法还具有@Rollback注释,以便在每次测试调用结束时回滚所有插入,更新和删除。所以我在这里遇到的问题是我想测试唯一的用户约束场景。通过使用具有相同电子邮件地址的用户对象第二次调用createUser(obj),我希望确保抛出UserManagementException异常。但是,由于它是事务性的,因此每当抛出异常时,事务都会在单元测试完成之前回滚,因此测试失败。以下是测试用例。

@Test
@Rollback
@Transactional
public void testUniqueCreateConsoleUser() {
    boolean success;
    ConsoleUser newUser;
    //first one
    userManagementDao.createConsoleUser(user);
    //second one. This shd throw a UserManagementException
    try {
        //now try and insert a new user with same email
        newUser = new ConsoleUser("Queen", "Kong", "king.kong@blah.com", "kingkong","Universal Studios", "America/Los_Angeles", false, null);
        userManagementDao.createConsoleUser(newUser);
        //if this passed this is a problem. Console users should have unique email address
        success = false;
    } catch (UserManagementException e) {
        success = true;
    }
    Assert.assertTrue(success);
}

奇怪的是,当我通过调试器运行它时,Assert.assertTrue()方法被正确调用,但测试最终失败。

我尝试的另一件事是在@Transactional注释中添加道具。我添加了流动的 @Transactional(noRollbackFor = UserManagementException.class),希望如果抛出异常,则不会在测试结束时调用回滚。我可能会以错误的方式接近这一点,因此围绕此类测试的任何想法或最佳实践都会非常昂贵。

注意:下面是stacktrace的片段..

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:695)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$afterReturning$org_springframework_transaction_aspectj_AbstractTransactionAspect

3 个答案:

答案 0 :(得分:0)

如果你可以确认抛出额外的回滚(例如 - 当spring执行插入时,当它看到它失败时,它是否已经将事务回滚?)那么你应该捕获回滚,或者配置spring not not推回交易。

也就是说,显然,Spring正在实施的回滚与单元测试中的预期回滚相冲突。这种回滚会混淆回滚注释,导致“unit-test / Spring ether”中出现意外抛出异常。

简单的解决方案:不要为此测试启用自动回滚。测试并不总是非常优雅。

答案 1 :(得分:0)

我建议首先从数据库中加载现有用户,然后尝试使用与该用户相同的电子邮件地址插入另一个用户,而不是插入用户,然后插入另一个用户。被找回了。如果是这样,您只需要这样做:

@Test(expected = UserManagementException.class)
public void insert_duplicate_user() throws Exception {

  // Read user from database
  final ConsoleUser user = dao.load(...);

  // Create new user with same email address.
  final ConsoleUser newUser = new ConsoleUser (...);
  newUser.setEmail(user.getEmail());

  // Write
  dao.createConsoleUser(newUser);

  /*
   * If you get here, there is a problem with your DAO logic
   * and a new user (with the same email was created).
   * So, we need to clean that up
   */

  // Delete new user  
  dao.deleteUser(newUser);
}

除非抛出UserManagementException,否则此测试将失败。

答案 2 :(得分:0)

从您的示例中很难说,但您似乎正在测试您的实际DAO实现。而不是让单元测试数据击中您的实际数据库,使用模拟实现或模拟框架来模拟您的DAO。然后,您可以以编程方式处理返回的数据,并将其转换为您想要的任何验证方案。