在集成測試中,對於保存數據到數據庫的方法,我們需要驗證是否數據被正確地保存到數據庫中。
對於使用 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 字段。
- 創建一個 final 類
DbTestUtil
,然後通過一個私有構造器阻止實例化。 - 在
DbTestUtil
中增加一個靜態方法resetAutoIncrementColumns()
,這個方法接受需要重置自增的表名,當前上下文,然後進行重置操作。
要實現這個類,我們需要
- 獲得
DataSource
的引用。 - 打開數據庫連接。
- 執行重置語句。
下面是一個 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");
}