如何基于 Spring testing framework + DBUnit 简单实现一个自动化测试工具?

@Test
@DataSets(setUpDataSet = "setUp.xls", expectDataSet = "expect.xls")
public void test() {
}

测试方法执行前,先loadsetUp.xls中数据到测试库,测试方法结束后,自动和expect.xls中数据做比对;excel里面保存的是表的数据,多个表以多个sheet页形式体现,sheet name即是table name;其实github上已经有个功能非常完善的工具Spring Test DBUnit,这里只是说下大体实现思路

定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSets {
String setUpDataSet() default "";
String expectDataSet() default "";
}

初始化XlsFileLoader和DataSourceDatabaseTester

dbunit-2.4.8中已经有现成的org.dbunit.util.fileloader.XlsDataFileLoader,蛋疼的是它是从getClass().getResource(filename)加载,建议重写一个,从ClassPathResource(filename).getURL()加载

一般项目构建都会配置dataSource,所以用DataSourceDatabaseTester再合适不过,它来管理测试过程中数据库连接的创建销毁,以及最最重要的,根据具体DatabaseOperation处理数据集;有个地方要注意一下,连接属性最好设置一下数据库类型,方式是实现IOperationListener接口中connectionRetrieved()加入下面代码,这个方法会在每次new或retrieved一个连接后立即调用

connection.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory())

配置bean

@Configuration
@ImportResource("classpath:/application-root-bean.xml")
@Profile("test")
public class ServiceTestConfig {

@Autowired
DataSource dataSource;

@Bean(name="databaseTester")
public DataSourceDatabaseTester dataSourceDatabaseTester() throws Exception {
DataSourceDatabaseTester databaseTester = new DataSourceDatabaseTester(dataSource());
databaseTester.setOperationListener(new CustOperationListener());
return databaseTester;
}

@Bean(name="xlsDataFileLoader")
public CustomXlsFileLoader xlsDataFileLoader() {
return new CustomXlsFileLoader();
}
}

实现TestExecutionListener

  • 首先prepareTestInstance()DI
  • 接着beforeTestMethod()中加载setUpDataSet
  • 最后afterTestMethod()中比对expectDataSet
public class ServiceTestExecutionListener implements TestExecutionListener {

private IDatabaseTester databaseTester;
private CustomXlsFileLoader xlsFileLoader;
private IDataSet expectDataSet;

public void prepareTestInstance(TestContext testCtx) throws Exception {
databaseTester = (IDatabaseTester) testCtx.getApplicationContext().getBean("databaseTester");
xlsFileLoader = (CustomXlsFileLoader) testCtx.getApplicationContext().getBean("xlsDataFileLoader");
}

public void beforeTestMethod(TestContext testCtx) throws Exception {
// Check for existence of DataSets annotation for the method under testing
DataSets dataSetAnnotation = testCtx.getTestMethod().getAnnotation(DataSets.class);
if ( dataSetAnnotation == null ) {
return;
}
String dataSetName = dataSetAnnotation.setUpDataSet();
if ( ! "".equals(dataSetName) ) {
IDataSet dataSet = xlsFileLoader.load(dataSetName);
databaseTester.setDataSet(dataSet);
databaseTester.onSetup();
}
String expectDataName = dataSetAnnotation.expectDataSet();
if ( ! "".equals(expectDataName)) {
expectDataSet = xlsFileLoader.load(expectDataName);
}
}

public void afterTestMethod(TestContext testCtx) throws Exception {
try {
if ( null != expectDataSet && null != databaseTester) {
IDatabaseConnection connection = databaseTester.getConnection();
connection.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
IDataSet actualDataSet = connection.createDataSet();
Assert.assertNotNull(actualDataSet);
for (String tableName : expectDataSet.getTableNames()) {
// 以expectDataSet中字段为准,比如CREATE_TIME, MODIFY_TIME这种字段比较是没有意义的
ITable filteredTable = DefaultColumnFilter.includedColumnsTable(actualDataSet.getTable(tableName),
expectDataSet.getTableMetaData(tableName).getColumns());
Assertion.assertEquals(expectDataSet.getTable(tableName), filteredTable);
}
}
} catch (Exception e) {
throw e;
} finally {
// Clear up testing data if exists
if (databaseTester != null) {
databaseTester.onTearDown();
}
}
}

public void beforeTestClass(TestContext testCtx) throws Exception {}
public void afterTestClass(TestContext testCtx) throws Exception {}

配置使用

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {ServiceTestConfig.class})
@TestExecutionListeners({ServiceTestExecutionListener.class})
@ActiveProfiles("test")
public abstract class AbstractServiceTest extends AbstractTransactionalJUnit4SpringContextTests {
}

Reference