20

我想使用弹簧测试框架测试休眠会话的 save() 方法。@Test 方法是:

@Test
@Transactional
public void testSave() {
    User expected = createUser();
    getGenericDao().currentSession().save(expected);
    User actual = getUser(generatedId);
    assertUsersEqual(expected,actual);
}

我想将用户刷新到数据库中。我希望我的用户在这种方法之后进入数据库

getGenericDao().currentSession().save(expected);

然后我想使用 spring 数据框架进入数据库并通过下一行获取这个保存的用户:

User actual = getUser(generatedId);

我尝试使用休眠刷新方法,例如:

currentSession().setFlushMode(MANUAL);
//do saving here
currentSession().flush();

它不会将我的用户刷新到数据库中!但是,如果我不使用 @Transactional spring 注释并将我的用户保存在程序化 spring 事务中,我将实现我想要的。不幸的是,保存到数据库中的用户没有回滚,因为没有弹簧@Transactional。因此,我的测试方法改变了后续测试方法的数据库和行为。

所以我需要将我的用户刷新到测试方法内的数据库中(而不是最后),并在测试方法结束时回滚对数据库的所有更改。

更新建议准备方法如下:

@Transactional
public void doSave(User user){
    getGenericDao().currentSession().save(user);
}

并且在 testSave 中调用 doSave 什么也不做。执行此方法后,我在 db 中仍然没有用户。我设置断点并从命令行检查我的数据库。

更新非常感谢您的回复。问题是方法 flush() 没有将我的用户放入数据库。我尝试了 Isolation.READ_UNCOMMITTED 并且它没有将我的用户放入数据库。我可以实现我想要的,但前提是我关闭 @Test 方法上的 spring 事务并保存编程事务。但是,@Test 方法不会回滚,将保存的用户留给后续的@Test 方法。这里@Test 方法保存用户不像@Test 方法删除用户那样危险,因为它没有回滚。所以必须有对@Test 方法的spring 事务支持,我无论如何都不能将我的用户(或删除)放入数据库中。实际上,只有在 @Test 方法结束并且 @Test 方法的事务被提交后,用户才被放入(或删除)到数据库中。所以我想将我的用户保存到@Test方法中间的db中,并在@Test方法结束时回滚它

谢谢!

4

4 回答 4

12

最后我坚持以下解决方案:

首先,我的@Test方法不在弹簧@Transactional支持范围内运行。请参阅这篇文章以了解它可能有多危险。接下来,我没有@Repository在方法中使用 bean ,而是@Test自动装配@Service使用@Transactional注释的 bean。奇迹就是这样的@Test方法

@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testSave() {
    Answer created = createAnswer();
    Long generatedId = answerService.save(created);
//at this moment answer is already in db
    Answer actual=getAnswerById(generatedId);
... }

将我的 Answer 对象放入数据库(就在 之后answerService.save(created);),然后方法getAnswerById转到 DB 并提取它以检查保存是否正确。
为了消除在方法中对数据库所做的更改,@Test我通过以下方式重新创建数据库JdbcTestUtils.executeSqlScript

于 2013-06-28T09:42:26.723 回答
6
  1. 在这里查看有关测试的警告@TransactionalSpring Pitfalls: Transactional tests considered dangerous)。我曾经在我的服务测试和控制器@org.springframework.test.context.jdbc.Sql中重新填充数据库。@Transactional
  2. ConstraintViolationException仅在提交事务时才抛出具有无效数据的控制器更新测试。所以我找到了 3 个选项:
    • @Commit2.1 用或注释测试@Transactional(propagation = Propagation.NEVER)。请注意数据库更改。
    • 2.2 使用TestTransaction

代码:

     TestTransaction.flagForCommit();
     TestTransaction.end();
  • 2.3 使用TransactionTemplate

代码:

    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    @Test(expected = Exception.class)
    public void testUpdate() throws Exception {
        TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        String json = ...
        transactionTemplate.execute(ts -> {
            try {
                mockMvc.perform(put(REST_URL + USER_ID)
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(json))
                    .andExpect(status().isOk());
                ...
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        });
于 2017-09-25T22:22:01.237 回答
3

您还应该注意导入的包:在我的情况下,我导入了

导入 javax.transaction.Transactional;

代替

导入 org.springframework.transaction.annotation.Transactional;

于 2014-06-10T11:53:43.197 回答
1

如果flush不起作用,那么它在很大程度上取决于您的数据库隔离级别。

IsolationACID数据库的属性之一,它定义了一个操作所做的更改如何/何时对其他并发操作可见。

我相信您的隔离级别设置为Read Committedor Repeatable Read

于 2013-06-23T19:48:40.357 回答