Spring 容器管理所有業務物件,DI 解耦合同時給單元測試帶來挑戰:如何在測試中取得 Spring 管理的 Bean?Spring 提供 spring-mock.jar 中的測試支援類別,其中 AbstractTransactionalDataSourceSpringContextTests 是最常用的事務性測試基類。本文詳細說明設定與實戰模式。
spring-mock.jar:Spring 測試支援類別 (含 AbstractTransactionalDataSourceSpringContextTests)naming-factory-dbcp.jar:JDBC 連線池工廠類別 (提供 DataSource)TestCase) 或 JUnit 4+ (需遷移寫法)特性:
TestCase,整合 JUnitsetDefaultRollback(false) 提交事務applicationContext、jdbcTemplate 等便利屬性
protected String[] getConfigLocations() {
return new String[] {
"service-applicationContext.xml",
"dao-applicationContext.xml"
};
}
注意:
TransactionManager Bean 在所有配置檔中 只能定義一個
public abstract class MyDependencyInjectionSpringContextTests
extends AbstractTransactionalDataSourceSpringContextTests {
protected ServiceContext context;
/** 覆寫父類 onSetUpBeforeTransaction,統一前置操作 */
protected void onSetUpBeforeTransaction() throws Exception {
super.onSetUpBeforeTransaction();
// 統一初始化邏輯:資料清理、通用 Mock 資料準備等
beforeSetUp(); // 交給子類具體實作
}
/** 子類必須實作的具體前置邏輯 */
protected abstract void beforeSetUp() throws Exception;
/** 配置檔位置,子類可覆寫擴充 */
protected String[] getConfigLocations() {
return new String[] {
"service-applicationContext.xml",
"dao-applicationContext.xml"
};
}
}
public class MockServiceTest extends MyDependencyInjectionSpringContextTests {
private Mock mock;
private MockService service;
/** 具體前置:準備測試資料、取得 Spring Bean */
protected void beforeSetUp() throws Exception {
mock = new Mock();
mock.setName("test");
mock.setType(1);
// 從 Spring 容器取得服務 Bean
service = (MockService) applicationContext.getBean("mockService");
}
/** 測試新增功能 */
public void testAddMock() {
service.addMock(mock);
// Hibernate 新增後主鍵回填,判斷 id 非空即成功
assertNotNull("測試 addMock 方法失敗", mock.getMockId());
}
/** 主程式入口,支援 IDE 直接 Run As JUnit Test */
public static void main(String[] args) {
junit.textui.TestRunner.run(MockServiceTest.class);
}
}
| 實踐 | 說明 |
|---|---|
| 繼承事務性基類 | 自動回滾保證測試隔離,無需手動清理資料庫 |
| 分層基類封裝 | 統一配置檔載入、通用 SetUp 邏輯,減少重複代碼 |
| getConfigLocations 集中管理 | 單一維護點,新增模組配置只改一處 |
| TransactionManager 單一定義 | 避免「No unique TransactionManager」啟動錯誤 |
| 測試資料在 beforeSetUp 建立 | 每個測試方法獨立資料,避免測試間污染 |
| 使用 applicationContext.getBean() 取得服務 | 而非手動 new,確保測試真實依賴注入鏈 |
| 斷言使用 JUnit Assert 而非 System.out | 自動化測試報表可讀、CI 整合友善 |
Spring 2.5 引入 org.springframework.test.context.junit4.SpringJUnit4ClassRunner,支援註解式配置:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"service-applicationContext.xml", "dao-applicationContext.xml"})
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)
@Transactional
public class MockServiceTestJUnit4 {
@Autowired private MockService service;
private Mock mock;
@Before
public void setUp() {
mock = new Mock();
mock.setName("test");
mock.setType(1);
}
@Test
public void testAddMock() {
service.addMock(mock);
assertNotNull(mock.getMockId());
}
}
註解式更簡潔、不需繼承特定基類、支援 @Autowired 直接注入測試類欄位。
Spring 測試支援將 DI 容器能力延伸至測試階段。掌握 AbstractTransactionalDataSourceSpringContextTests 事務回滾機制、分層基類封裝模式、配置檔集中管理,即可編寫乾淨、隔離、可維護的整合測試。現代專案建議遷移至 JUnit 4+ 註解式寫法,更符合當前開發習慣。