10

有关 testing 的 spring 文档中,它指出:

测试 ORM 代码时避免误报

当您测试涉及 ORM 框架(如 JPA 或 Hibernate)的代码时,请在更新会话状态的测试方法中刷新底层会话。未能刷新 ORM 框架的底层会话可能会产生误报:您的测试可能会通过,但相同的代码会在实时生产环境中引发异常。在以下基于 Hibernate 的示例测试用例中,一种方法演示了误报,另一种方法正确公开了刷新会话的结果。

有人可以解释为什么我需要调用flush吗?

4

4 回答 4

3

好吧,您实际上跳过了有趣的部分,例如 :) 这里是:

// ...

@Autowired
private SessionFactory sessionFactory;

@Test // no expected exception!
public void falsePositive() {
    updateEntityInHibernateSession();
    // False positive: an exception will be thrown once the session is
    // finally flushed (i.e., in production code)
}

@Test(expected = GenericJDBCException.class)
public void updateWithSessionFlush() {
    updateEntityInHibernateSession();
    // Manual flush is required to avoid false positive in test
    sessionFactory.getCurrentSession().flush();
}

// ...

此示例试图说明的是,除非您实际上flush使用会话(AKA 一级缓存)将内存中的更改与数据库同步,否则您并没有真正测试数据库集成,并且可能不会测试真正的预期行为或错过问题。

例如,数据库可能会因为违反约束而返回错误,如果您没有访问数据库,您将不会表现出这种正确的行为,就像falsePositive()上面的测试方法一样。这个测试方法应该失败,或者期待一个异常但会通过。另一方面,使用冲洗的另一种测试方法确实测试了真实行为。因此需要flush.

于 2010-08-25T03:04:43.493 回答
1

使用 @Transactional 注释 Spring 测试很方便,但这不是您的生产代码的执行方式。@Transactional 注释将在运行您的测试方法之前启动一个事务,并在测试方法完成时将其回滚。

虽然提交之前是刷新,但回滚不是,因此手动刷新是一种安全机制,可确保所有实体更改都转换为 SQL 语句。

更合适的设计是像这样显式地绘制事务边界:

@Test
public void testRootObjects() {

    final Company newCompany = new Company();
    newCompany.setName("TV Company");

    final Long companyId = transactionTemplate.execute(new TransactionCallback<Long>() {
        @Override
        public Long doInTransaction(TransactionStatus transactionStatus) {
            entityManager.persist(newCompany);
            return newCompany.getId();
        }
    });
    Company detachedCompany = transactionTemplate.execute(new TransactionCallback<Company>() {
        @Override
        public Company doInTransaction(TransactionStatus transactionStatus) {
            Company attachedCompany = entityManager.find(Company.class, companyId);
            assertEquals(newCompany, attachedCompany);
            assertEquals(newCompany.hashCode(), attachedCompany.hashCode());
            return attachedCompany;
        }
    });
    assertEquals(newCompany, detachedCompany);
    assertEquals(newCompany.hashCode(), detachedCompany.hashCode());
}

TransactionTemplate将提交您的代码,因此无需手动刷新。

如果您通过它们的接口调用@Transactional 服务方法,则根本不需要事务模板,因为您正在调用将调用 TransactionInterceptor 的 Spring 代理(假设您指示 Spring 了解事务注释:),因此事务将是代表您开始/承诺。

于 2014-05-15T09:06:05.567 回答
1

Spring 文档使用了错误的概念。已经很清楚了

但是相同的代码在实时生产环境中引发异常

维基百科来了

II 型错误,也称为“第二类错误”、β 错误或“假阴性”:当原假设实际上不正确时未能拒绝原假设的错误。这方面的一个例子是,如果测试显示一个女人没有怀孕,而实际上她怀孕了。

如果看到 Spring 提供的样例,生产环境抛出异常(A GenericJDBCException),但未检测到。为了查看,您必须在使用某些 ORM 提供程序时调用底层提交。

XUnit 测试模式定义

即使被测系统工作不正常,测试也通过的情况。

所以正确的概念是 falseNegative

@Test // no expected exception!
public void falseNegative() {
于 2010-08-25T04:15:13.357 回答
0

有人检查过注释@TransactionConfiguration 吗?如果您在项目中使用 @Transactional 注释驱动,您只需在测试用例中设置@TransactionConfiguration(defaultRollback = false, transactionManager = "YourTransactionManager") 即可完美运行,希望这会对您有所帮助。

于 2018-01-04T06:50:27.070 回答