【问题标题】:How to rollback a specific record after a test?测试后如何回滚特定记录?
【发布时间】:2011-06-30 14:18:32
【问题描述】:

我有一个修改一些数据库内容的 spock / spring 测试,我想知道如何回滚该记录。

目前我执行原始 sql 语句,保存字段数据,并在成功测试后恢复该数据。但我的猜测是,这可以用更简单的方式完成吗?

@ContextConfiguration(locations = "classpath*:applicationContext-test.xml")
class RepositoryTest extends Specification {

    @Shared sql = Sql.newInstance("jdbc:sqlserver://...")
    ResourceRepository resourceRepository;

    def "Save test"() {
        setup:
        // copy the row before we do changes! we need to restore this later on!
        def row = sql.firstRow("select id, content, status from table-name where id = 12345")

        when:
        ...

        then:
        ..

        cleanup:
        sql.execute("""
                    update table-name set content = ${row.content}, status = ${row.status}
                    where id = ${row.id}
                    """)
    }
}

【问题讨论】:

  • 您真的要回滚特定记录,而不是测试方法所做的任何事情吗?

标签: testing groovy spock


【解决方案1】:

我发现最好的方法是:

  • 开始测试
  • 开始交易
  • (可选)使用 DBUnit 之类的工具加载测试所需的任何数据库数据
  • 运行测试
  • 回滚事务

请注意,所有数据库操作都发生在同一个事务中。因为此事务在测试结束时回滚(即使抛出异常),所以数据库在测试结束时应始终处于与开始时相同的状态。

Spring 提供了一些非常有用的类,它们将负责启动和回滚每个测试的事务。如果您使用 Spring 和 JUnit4 并且不介意您的测试类必须扩展 Spring 类,那么最简单的选择可能是扩展 AbstractTransactionalJUnit4SpringContextTests

// Location of the Spring config file(s)
@ContextConfiguration(locations = {"/application-context-test.xml", "classpath*:/application-context-persistence-test.xml"})

// Transaction manager bean name
@TransactionConfiguration(transactionManager = "hsqlTransactionManager", defaultRollback = true)
@Transactional(propagation=Propagation.REQUIRES_NEW)
public class MyTransactionalTests extends AbstractTransactionalJUnit4SpringContextTests {

    @Test
    public void thisWillRunInATransactionThatIsAutomaticallyRolledBack() {}
}

如果你不想扩展 Spring 类,你可以配置一个测试运行器,而不是使用注解。 Spring 还支持许多其他主要的单元测试框架和旧版本的 JUnit。

【讨论】:

  • 因为 Spock 直接支持 Spring 的 TestContext 框架,所以您既不需要基类也不需要自定义运行器。可以省略 @TransactionConfiguration 以支持将 bean 命名为“transactionManager”。要使 Groovy 的 Sql 类与 Spring 的事务一起工作,您必须使用 Spring 提供的数据源对其进行初始化。 Sql 类不会像 Spring 一样执行异常转换这一事实可能会影响事务回滚的情况。
【解决方案2】:

如果您在测试中混合使用 Groovy 的 Sql 和 Spring 的注解(@RunWith、@ContextConfiguration、@Transactional、@Rollback,...),您可能需要使用 org.springframework.jdbc.datasource 包装数据源.TransactionAwareDataSourceProxy。

<bean id="db-dataSourceReal" 
   class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName" value="jdbc/foo" />
  <property name="resourceRef" value="true" />
  <property name="lookupOnStartup" value="true" />
</bean>

<bean id="db-dataSource"
   class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
  <constructor-arg ref="db-dataSourceReal" />
</bean>

然后使用 TransactionAwareDataSourceProxy 作为 Groovy Sql 的数据源。例如,在您的 Test 类中(在本例中使用 maven 的故障安全插件),

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations=[
        "classpath*:applicationContext-test.xml"
])
class SimplePojoDaoIT {

    @Resource(name="dao-pojoDao")
    PojoDao pojoDao

    @Test
    @Transactional("transactionManager")
    @Rollback
    public void testShouldPersistToDB(){

      SomePojo pojo = new SomePojo()
      pojo.with{
        id = 5
        name = 'foo'
      }

      pojoDao.persist(pojo)

      def sql = new Sql(pojoDao.dataSource)
      sql.rows("select * from POJO_TBL where id = :id", [['id':pojo.id]]).each{ row ->
//      println row
        pojo.with{
          assertEquals(id, row.ID.longValue())
          assertEquals(name, row.NAME)
        }
      }
    }
}

【讨论】:

    【解决方案3】:
    CREATE TABLE table_name
    (
       id        NUMBER,
       content   NUMBER,
       status    NUMBER
    );
    
    INSERT INTO table_name
         VALUES (1, 2, 3);
    
    INSERT INTO table_name
         VALUES (4, 5, 6);
    
    INSERT INTO table_name
         VALUES (7, 8, 9);
    
    COMMIT;
    

    在运行测试之前,将此 SELECT 生成的字符串存储在 VARCHAR2(4000) 变量中,并且在测试之后您只需执行该字符串:

    SELECT    'UPDATE TABLE_NAME SET CONTENT = '
           || CONTENT
           || ', STATUS = '
           || STATUS
           || ' WHERE ID = '
           || ID
      FROM TABLE_NAME
     WHERE ID = 1;
    

    在我之前的示例中,我假设要备份的记录的 ID = 1。 在本例中,该字符串包含以下 UPDATE 语句:

    UPDATE TABLE_NAME SET CONTENT = 2, STATUS = 3 WHERE ID = 1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-26
      • 1970-01-01
      • 2012-11-26
      • 1970-01-01
      • 2018-10-10
      相关资源
      最近更新 更多