Spring Test 中重置自增字段

在集成測試中,對於保存數據到數據庫的方法,我們需要驗證是否數據被正確地保存到數據庫中。

對於使用 Spring 的項目,可以使用 Spring Test DbUnit 和 DbUnit 進行測試。

然而,在數據對比的時候,自增的字段就成了一個障礙,因爲自增字段是自動生成的,在測試的時候,我們無法確定下一個插入數據的自增字段的值。

嘗試忽略自增 id

首先,我們考慮忽略自增 id,在 @ExpectedDatabase 註解中設置 assertionMode = DatabaseAssertionMode.NON_STRICT ,就會忽略數據集中沒有顯示聲明的字段。但是每一行數據都必須擁有一致的字段。比如

<dataset>
    <clazz name="clazz1"/>
    <clazz name="clazz2"/>
</dataset>

這看起來沒有什麼大問題,id 會在插入數據的時候自動生成。但是,假設我們有一個學生表,每個學生對應一個班級,問題就來了。比如我們有如下數據集:

<dataset>
    <clazz name="clazz1"/>
    <clazz name="clazz2"/>

    <student name="student1"/>
    <student name="student2"/>
</dataset>

這個數據集無法完成初始化,因爲我們不知道班級的 id,學生數據就無法插入數據庫。所以,我們無法在測試中忽略自增 id。

使用 @DirtiesContext

一個很容易的方法就是使用 Spring 的 @DirtiesContext 註解,然後設置 classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD ,這樣會在每個測試方法開始前重新載入新的上下文,包括我們的數據庫。這種配置看起來如下

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersistenceContext.class})
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class })
@DbUnitConfiguration(dataSetLoader = ColumnSensingReplacementDataSetLoader.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class ITTodoRepositoryTest {
}

即使這種方法很簡單,也解決了我們的問題,但顯而易見的是這會降低我們測試的效率,因爲每一個測試方法都重新載入一個新的上下文,如果對測試效率有要求,這種方法就不是太適用了。

創建一個能重置自增的幫助類

我們可以創建一個幫助類,來重置指定表的自增 id 字段。

  1. 創建一個 final 類 DbTestUtil ,然後通過一個私有構造器阻止實例化。
  2. DbTestUtil 中增加一個靜態方法 resetAutoIncrementColumns() ,這個方法接受需要重置自增的表名,當前上下文,然後進行重置操作。

要實現這個類,我們需要

  1. 獲得 DataSource 的引用。
  2. 打開數據庫連接。
  3. 執行重置語句。

下面是一個 DbTestUtil 示例

public final class DbTestUtil {
    private static final String resetSqlTemplate = "ALTER TABLE %s ALTER COLUMN id RESTART WITH 1";

    private DbTestUtil() { }

    public static void resetAutoIncrementColumns(ApplicationContext applicationContext,
                                                 String... tableNames) throws SQLException {
        DataSource dataSource = applicationContext.getBean(DataSource.class);
        try (Connection dbConnection = dataSource.getConnection()) {
            for (String resetSqlArgument : tableNames) {
                try (Statement statement = dbConnection.createStatement()) {
                    String resetSql = String.format(resetSqlTemplate, resetSqlArgument);
                    statement.execute(resetSql);
                }
            }
        }
    }
}

這裏的重置語句是 H2 數據庫的語法。有了這個方法後,我們在測試前就可以進行調用。

@Before
public void setUp() throws SQLException {
    DbTestUtil.resetAutoIncrementColumns(applicationContext, "student", "clazz");
}
發佈了50 篇原創文章 · 獲贊 26 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章