在测试的时候,我们有时需要为数据库准备二进制数据,比如遇到ORACLE的 BLOB 类型的字段。BLOB可以存放任意的二进制数据,比如图片等。dbUnit从2.1版开始加入了对BLOB的支持(原文:“Ability to import external file for binary data types like BLOB; the value can be either a qualified URL or a file path name.”)但却没有给出任何文档说明何为“a qualified URL or a file path name”。而且看似要为每个二进制数据准备不同的文件,这也容易加大测试文件管理的复杂度,特别是有时候我们也许只需要输入几个byte的数据,这样我们就需要对dbUnit做一些进一步的开发来支持直接在种子文件中处理二进制数,其中主要利用ReplacementDataSet这个类。
在详细说明如何支持二进制数之前先介绍以下如何支持空数据,假设种子文件中存在以下内容:
<?xml version='1.0' encoding='UTF-8'?> <DATASET> <ACCOUNT id="1" username="Drew" password="[NULL]" /> …… </DATASET>
这条记录的密码允许为空,‘[NULL]’不是dbUnit的关键字,是我们自己定义的。在被提交给dbUnit之前,我们必须将它过滤掉,并用真正的null替代:
@Override
protected IDataSet getDataSet() throws Exception {
ReplacementDataSet dataSetnew FlatXmlDataSet((
new ReplacementDataSet(
new FileInputStream(“dataSetFilePath”))));
dataFilter(dataSet);
return dataSet;
}
private void dataFilter(ReplacementDataSet dataSet) throws DataSetException {
// Filter NULL
dataSet.addReplacementObject("[NULL]", null);
}
这样dataSet中的,被标记为”[NULL]”的值就都被null替换掉了。用这里我们用最简单的方式演示了ReplacementDataSet类的作用。类似的方法我们也可以让dbUnit替换我们需要转换成二进制的数据。现假设种子数据为以下形式:
<?xml version='1.0' encoding='UTF-8'?>
<DATASET>
<ACCOUNT id="1"
username="Drew"
password="[NULL]"
logonhours="[BINARY:e0ff01e0ff01e0ff01e0ff01e0ff01]"
/>
……
</DATASET>
和上例一样,这里的[BINARY:….]是我们自己定义的。我们只需要在提交给dbUnit之前找到并替换掉它就可以了,让我们扩充一下dataFilter方法,代码如下:
private void dataFilter(ReplacementDataSet dataSet) throws DataSetException {
static final String replacementPattern = "^\\[([^]]*)\\]$";
static final String dataTypePattern = "^\\w+:*";
static final String DATA_TYPE_NULL = "NULL";
static final String DATA_TYPE_BINARY = "BINARY";
static final String DATA_TYPE_FILE = "FILE";
// Filter NULL
dataSet.addReplacementObject("[" + DATA_TYPE_NULL +"]", null);
ITableIterator tables = dataSet.iterator();
// Fetch tables(1)
while(tables.next()) {
ITable table = tables.getTable();
ITableMetaData metaData = table.getTableMetaData();
Column[] columns = metaData.getColumns();
// Fetch rows (2)
int rowCount = table.getRowCount();
for(int row=0; row<rowCount; row++){
// Fetch columns (3)
for(int i=0; i<columns.length; i++) {
Column c = columns[i];
String name = c.getColumnName();
if(name.equals("logonhours")){
logger.info("logonhours!!");
}
// Check value (4)
String value = (String)table.getValue(row, name);
if(null != value) {
// Find "[TYPE:VALUE]"
Pattern p=Pattern.compile(replacementPattern);
Matcher m=p.matcher(value);
if(m.find()) {
String matchedData = m.group(0);
String data= matchedData.substring(1,matchedData.length()-1);
p = Pattern.compile(dataTypePattern);
m = p.matcher(data);
if(m.find()) {
String type = m.group(0);
String v = data.substring(type.length());
type = type.substring(0,type.length()-1);
// Filter Binary data (5)
if(type.toUpperCase().equals(DATA_TYPE_BINARY) {
byte[] bin = new byte[v.length()/2];
for(int index = 0; index<v.length()/2; index++) {
bin[index] =Integer.valueOf(v.substring(index*2,index*2+2),16).byteValue();
}
dataSet. addReplacementObject(matchedData,bin);
}
// Filter Binary data end
if(type.toUpperCase().equals(DATA_TYPE_FILE)) {
logger.info( "Load data from file:"+v);
} else {
logger.warn("Unsupport data type.");
}
}
}
}
}
}
}
}
- 我们读取种子文件中的每一个表
- 然后读取每个表中的记录
- 再获得每条记录的字段
- 最后我们检查这些字段
- 并过滤出我们定义的关键字
Filter Binary data 代码,们取出BINARY后面的字串并转换成byte[]类型,然后再放回去,这样就将文本输入转换成了二进制输入。
这个方法还有些不完善的地方,首先,它只能简单的处理16进制输入,不能支持十进制,如果要支持十进制的话还需要进一步转换。其次,用类似的方法还可以处理文件输入,将输入定义成’[FILE:path_to_file]’的形式,然后在灰色代码部分实现对文件的读取并转换成二进制输入就可以。
虽然dbUnit的设计初衷很好,但是不知为什么开发的很不完善,如果不做二次开发几乎是无法使用,但是只要经过合适的二次开发,它还是可以很好的满足需求。