【问题标题】:How to rollback a database transaction when testing services with Spring in JUnit?在 JUnit 中使用 Spring 测试服务时如何回滚数据库事务?
【发布时间】:2011-05-09 04:40:21
【问题描述】:

我测试我的 DAO 和服务没有问题,但是当我测试 INSERTs 或 UPDATEs 时,我想回滚事务而不影响我的数据库。

我在我的服务中使用@Transactional 来管理交易。我想知道,是否有可能知道事务是否会正常,但回滚以防止更改数据库?

这是我的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring.cfg.xml")
@TransactionConfiguration(defaultRollback=true)
public class MyServiceTest extends AbstractJUnit38SpringContextTests  {
    @Autowired
    private MyService myService;

    @BeforeClass
    public static void setUpClass() throws Exception {
    }

    @AfterClass
    public static void tearDownClass() throws Exception {
    }

    @Test
    public void testInsert(){
        long id = myService.addPerson( "JUNIT" );
        assertNotNull( id );
        if( id < 1 ){
            fail();
        }
    }
}

问题是这个测试会失败,因为事务被回滚了,但是插入是可以的! 如果我删除@TransactionConfiguration(defaultRollback=true),则测试通过,但会在数据库中插入一条新记录。

@Test
@Transactional
@Rollback(true)
public void testInsert(){
    long id = myService.addPerson( "JUNIT" );
assertNotNull(id);
if( id < 1 ){
        fail();
    }
}

现在可以正确测试通过,但忽略回滚并将记录插入数据库。 很明显,我在 myService 中用 @Transactional 注释了方法 addPerson()。 为什么回滚会被忽略?

【问题讨论】:

    标签: java hibernate spring transactions junit


    【解决方案1】:

    您需要将事务边界扩展到测试方法的边界。您可以通过将您的测试方法(或整个测试类)注释为@Transactional 来做到这一点:

    @Test 
    @Transactional
    public void testInsert(){ 
        long id=myService.addPerson("JUNIT"); 
        assertNotNull(id); 
        if(id<1){ 
            fail(); 
        } 
    } 
    

    您也可以使用这种方法来确保在回滚之前正确写入数据:

    @Autowired SessionFactory sf;
    
    @Test 
    @Transactional
    public void testInsert(){ 
        myService.addPerson("JUNIT"); 
        sf.getCurrentSession().flush();
        sf.getCurrentSession().doWork( ... check database state ... ); 
    } 
    

    【讨论】:

    • 嗨,现在测试通过了,但是回滚被忽略了。我在“testAddPerson”和“addPerson”上有@Transactional。
    • 如果我从 myService 中删除 @Transactional,则测试没有活动事务,所以我认为 @Transactional over "testAddPerson" 不起作用...
    • @blow:我刚刚注意到你有extends AbstractJUnit38SpringContextTests。不需要,因为您使用 @RunWith 进行了 JUnit 4 测试。
    • 你是对的。没有 AbstractJUnit38SpringContextTests 和 AbstractTransactionalJUnit38SpringContextTests 一切正常。谢谢。
    • 我还必须添加@TestExecutionListeners({TransactionalTestExecutionListener.class})
    【解决方案2】:

    看看

    http://static.springsource.org/spring/docs/2.5.x/reference/testing.html

    特别是第 8.3.4 节

    Spring 有一些用于测试的类,它们会将每个测试包装在一个事务中,因此数据库不会更改。如果您愿意,您也可以更改该功能。

    编辑——根据你的更多信息,你可能想看看

    AbstractTransactionalJUnit38SpringContextTests 在

    http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/context/junit38/AbstractTransactionalJUnit38SpringContextTests.html

    【讨论】:

    • 谢谢,这很有用,我在我的实现中发现了一个小问题。现在我有另一个问题,回滚被忽略了
    【解决方案3】:

    在课前使用以下注释:

    @TransactionConfiguration(transactionManager = "txManager",defaultRollback = true)
    @Transactional
    

    这里txManager 是应用上下文的事务管理器。

    这里txManager 是来自application context 的事务管理器的实例或bean id。

    <!-- Transaction Manager -->
        <bean id="txManager"
              class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
        </bean>
    
        <tx:annotation-driven transaction-manager="txManager" />
    

    将您的代码添加到setUp() 方法中,这将在测试开始时执行,最后的包装代码应放入最后执行的teatDown() 方法中。或者你也可以使用@Before@After 注解来代替它。

    【讨论】:

      【解决方案4】:

      如果你的

      myService.addPerson("JUNIT"); 
      

      方法被注释为@Transactional,你会得到一些不同的类型或错误来尝试解决这个问题。所以你最好只测试 DAO 方法。

      【讨论】:

        猜你喜欢
        • 2016-01-11
        • 2015-06-19
        • 1970-01-01
        • 2012-01-27
        • 1970-01-01
        • 1970-01-01
        • 2015-06-13
        • 2013-05-08
        • 2011-10-04
        相关资源
        最近更新 更多