单元测试遵守3A原则(Arrange, Act, Assert)即准备,执行,验证。
在准备测试数据时,若是使用硬编码进行new 对象,修改数据等操作。会遇到几个问题:测试数据的维护性不好,不方便管理,不够直观,数据可重用性低,对比字段多时不方便验证。因此测试数据需要从硬编码中分离出来,使用文档管理。 CSV简洁方便,二进制,支持多种格式编辑,支持excel,libreoffic编辑,数据版本易于维护。
在assert验证期间,当一个对象的属性很多时,Assert.assertEquals()过程中部分人为了麻烦,只是验证部分重要数据,这会造成验证数据的不完整性。为了避免重复这种比较操作,需要把此部分提取出来,作为模块运行。
csv测试框架实现:
csv格式(title列,必需与数据库字段相同,实体对象属性相似)
unitTest id account amount
before 1 张三 20
after 1 张三 30
before测试准备数据,after执行后进行的验证数据
核心类
testDataContext:测试数据载体
public String tableName;
public String idColumn;
public String dbType;
public String csvPath;
public String csvCharset;
public String[] classNameArray;
public String[] columnNameArray;
public Map
public Map
testDataManager:测试数据的加载维护管理
loadTestDataCsv(String csvPath, TestDataContext dataContext) ;
putDataToDb(QueryRunner queryRunner, TestDataContext dataContext, Long... idList)
assertDataById(QueryRunner queryRunner, TestDataContext dataContext, ConvertMetaAndCacheKey convertMeta, boolean changeExpect, Set
skipAssertSet:过滤不需要比较的属性字段
assertConvertManager:处理DO,csv,map之间的转换,初始化准备数据。把所有数据转换成Map对象来进行处理。
prepareCsvColumnToClassField(String table, String[] columnArray, Class> clazz) 初始化关联csv的列与对象属性。
Map
Map
Map
for (String field : fieldClassMap.keySet()) {
fieldNameMap.put(field.toLowerCase(), field);
}
for (String column : columnArray) {
column = column.toLowerCase();
String columnKeyLower = StringUtils.remove(column, "_").toLowerCase();
String fieldName = fieldNameMap.get(columnKeyLower);
if (StringUtils.isBlank(fieldName)) {
continue;
}
columnFieldMap.put(column, fieldName);
}
return columnFieldMap;
}
private Map
String className = clazz.getName();
Map
if (FieldClassMap == null) {
FieldClassMap = new HashMap
BeanInfo bean = null;
try {
bean = Introspector.getBeanInfo(clazz, Object.class);
} catch (IntrospectionException e) {
log.error("{msg:'类属性转map异常',className:" + className + "}", e);
}
PropertyDescriptor[] props = bean.getPropertyDescriptors();
for (PropertyDescriptor prop : props) {
if (prop.getReadMethod() == null || prop.getWriteMethod() == null) {
continue;
}
FieldClassMap.put(prop.getName(), prop.getPropertyType());
}
}
cacheForFieldClassMap.put(className, FieldClassMap);
return FieldClassMap;
}
public Map
public Map
public
AssertUtils assertEquals工具类
public static void assertMap(Map
if (skipAssertSet == null) {
skipAssertSet = new HashSet<>();
}
for (Map.Entry
if (skipAssertSet.contains(entry.getKey())) {
continue;
}
if (!changeExpect) {
Assert.assertEquals(entry.getKey(), entry.getValue(), doMap.get(entry.getKey()));
} else {
Assert.assertEquals(entry.getKey(), doMap.get(entry.getKey()), entry.getValue());
}
}
}
以上是粗略代码。
MemberTest:
Arrange:
@PostConstruct
public void init() {
if (dataContext == null) {
dataContext = dataManager.loadCsvDataContextToMap("/csv/member.csv", "member");
convertManager.prepareCsvColumnToClassField(dataContext.tableName, dataContext.columnNameArray, Member.class);
}
}
public void test_updateAmount_增加用户金额(){
Long id = 1L;
dbQueryUtil.update("delete from member where id=" + id);
dataManager.putDataToDb(dbQueryUtil, dataContext, id);
//Act:
//执行增加方法
//Assert
testManager.assertDbById(dbQueryRunner, dataContext, dataContext.dataAfterTest, cm, id, skipFieldList);
}
整个测试方法相当简洁,没有很多重复的assertEquals,验证了所有字段,保证测试完整性。
流程:testDataManager(加载csv) --> assertConvertManager(初始化,并缓存到map) -->assertConvertManager(数据之间的转换) --> AssertUtils(转换成map,进行Assert.assertEquals)