在测试的时候,我们有时需要为数据库准备二进制数据,比如遇到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的设计初衷很好,但是不知为什么开发的很不完善,如果不做二次开发几乎是无法使用,但是只要经过合适的二次开发,它还是可以很好的满足需求。