【问题标题】:Rollback transaction after @Test@Test 后回滚事务
【发布时间】:2012-09-19 12:59:28
【问题描述】:

首先,我在 StackOverflow 上找到了很多关于此的主题,但没有一个对我有真正的帮助,所以很抱歉提出可能重复的问题。

我正在使用 spring-test 运行 JUnit 测试,我的代码如下所示

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {})
public class StudentSystemTest {

    @Autowired
    private StudentSystem studentSystem;

    @Before
    public void initTest() {
    // set up the database, create basic structure for testing
    }

    @Test
    public void test1() {
    }    
    ...  
}

我的问题是我希望我的测试不会影响其他测试。 所以我想为每个测试创建类似回滚的东西。 我已经为此搜索了很多,但到目前为止我什么也没找到。 我正在为此使用 Hibernate 和 MySql

【问题讨论】:

  • 回滚是什么意思?清理数据库?
  • 将其设置为与执行initTest后完全相同的状态

标签: java spring hibernate junit spring-test


【解决方案1】:

只需在测试顶部添加 @Transactional 注释即可:

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {"testContext.xml"})
@Transactional
public class StudentSystemTest {

默认情况下,Spring 将围绕您的测试方法和@Before/@After 回调启动一个新事务,并在最后回滚。它默认工作,在上下文中有一些事务管理器就足够了。

来自:10.3.5.4 Transaction management(我的粗体字):

在 TestContext 框架中,事务由 TransactionalTestExecutionListener 管理。请注意TransactionalTestExecutionListener 是默认配置的,即使您没有在测试类上明确声明@TestExecutionListeners。但是,要启用对事务的支持,您必须在由@ContextConfiguration 语义加载的应用程序上下文中提供PlatformTransactionManager bean。此外,您必须在测试的类或方法级别声明 @Transactional。

【讨论】:

  • 嗯,这个我试过了,还是不行,可能……问题是我没有定义PlatformTransactionManager,怎么办?
  • @java:你是如何修改数据库的?如果您使用的是 Jpa/Hibernate/JdbcTemplate/... 肯定有一些 PlatformTransactionManager。否则 Spring 怎么知道你的事务和数据库?
  • 此答案中的链接不再正确;请参阅 user2418306's answer below 以获取 Spring 文档中的正确链接和更多上下文。
  • 我不工作,每个方法都需要单独的事务,不能为全班做
  • 我正在测试一个表格的插入。使用@Transactional,甚至不会针对数据库发出插入命令(我可以看到,因为控制台具有hibernate show-sql true)。在许多情况下工作正常。但是我的一项测试检查数据库是否由于数据库约束而发出 DataAccessException。在这种情况下,测试失败,因为回滚发生在“内存中”并且数据库永远不会被调用。解决方案:在@Test方法级别使用@Transactional,而不是在类级别。
【解决方案2】:

除此之外: 试图修改 Tomasz Nurkiewicz 的回答被拒绝了:

此修改并未使帖子更易于阅读、更易于查找、更准确或更易于访问。更改要么完全是多余的,要么会严重损害可读性。


关于集成测试的相关文档部分的正确且永久的link。

要启用对事务的支持,您必须在通过 @ContextConfiguration 语义加载的 ApplicationContext 中配置 PlatformTransactionManager bean。

@Configuration
@PropertySource("application.properties")
public class Persistence {
    @Autowired
    Environment env;

    @Bean
    DataSource dataSource() {
        return new DriverManagerDataSource(
                env.getProperty("datasource.url"),
                env.getProperty("datasource.user"),
                env.getProperty("datasource.password")
        );
    }

    @Bean
    PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

此外,您必须在测试的类或方法级别声明 Spring 的 @Transactional 注释。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {Persistence.class, SomeRepository.class})
@Transactional
public class SomeRepositoryTest { ... }

使用@Transactional 注释测试方法会导致测试在默认情况下在测试完成后自动回滚的事务中运行。如果测试类使用@Transactional 进行注释,则该类层次结构中的每个测试方法都将在事务中运行。

【讨论】:

    【解决方案3】:

    提到添加@Transactional 的答案是正确的,但为简单起见,您可以只使用测试类extends AbstractTransactionalJUnit4SpringContextTests。

    【讨论】:

    • 在类级别添加注释'@Transactional'不起作用,为每个函数单独添加注释'@Transactional'有效,并且扩展AbstractTransactionalJUnit4SpringContextTests也有效
    【解决方案4】:

    我知道,我发布答案太晚了,但希望它可以帮助某人。另外,我刚刚解决了我在测试中遇到的这个问题。这是我在测试中得到的:

    我的测试课

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "path-to-context" })
    @Transactional
    public class MyIntegrationTest 
    

    上下文 xml

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
       <property name="driverClassName" value="${jdbc.driverClassName}" />
       <property name="url" value="${jdbc.url}" />
       <property name="username" value="${jdbc.username}" />
       <property name="password" value="${jdbc.password}" />
    </bean>
    

    我还是有问题,数据库没有被自动清理。

    当我将以下属性添加到 BasicDataSource 时问题已解决

    <property name="defaultAutoCommit" value="false" />
    

    希望对你有帮助。

    【讨论】:

    • 那么,您手动提交语句?您确定您的数据甚至已写入您的数据库吗?
    • 对于任何难以理解 Spring Transactions 的人,请确保您的数据源未设置为自动提交,否则您会疯狂地试图弄清楚为什么 @Transactional 似乎什么都不做。
    【解决方案5】:

    除了在@Test方法上添加@Transactional,还需要添加@Rollback(false)

    【讨论】:

    • 这回答了相反的问题,即如何在测试中不回滚事务。但很高兴知道,这就是我来寻找的。​​span>
    【解决方案6】:

    您需要使用 Spring 上下文和事务管理器来运行测试,例如,

    @RunWith(SpringJUnit4ClassRunner.class)  
    @ContextConfiguration(locations = {"/your-applicationContext.xml"})
    @TransactionConfiguration(transactionManager="txMgr")
    public class StudentSystemTest {
    
         @Test
         public void testTransactionalService() {
             // test transactional service
         }
    
         @Test
         @Transactional
         public void testNonTransactionalService() {
             // test non-transactional service
         }
    }
    

    有关详细信息,请参阅 Spring 参考的第 3.5.8. Transaction Management 章。

    【讨论】:

      【解决方案7】:

      您可以禁用回滚:

      @TransactionConfiguration(defaultRollback = false)
      

      例子:

      @RunWith(SpringJUnit4ClassRunner.class)
      @SpringApplicationConfiguration(classes = Application.class)
      @Transactional
      @TransactionConfiguration(defaultRollback = false)
      public class Test {
          @PersistenceContext
          private EntityManager em;
      
          @org.junit.Test
          public void menge() {
              PersistentObject object = new PersistentObject();
              em.persist(object);
              em.flush();
          }
      }
      

      【讨论】:

      • 这与 OP 的要求完全相反
      • 这应该是对已接受答案的评论。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-12
      • 2015-10-03
      相关资源
      最近更新 更多