上一篇杂感提到同事用Windbg提高代码覆盖率,我也做过相似的事情。也是临近项目结束,也是项目的发布标准对代码覆盖率有定量要求,不同的是这次的测试对象是.NET应用。这个项目应用了LINQ to SQL技术,代码中有许多ORM类。这些类是代码生成工具的产出,一个类对应数据库中的一张表,其属性(Property)对应表的字段。开发人员图方便,生成了所有表对应的类,其所有属性都支持读写(get/set)。不幸的是,这个应用只访问部分表,而且大多是只读访问。这导致这些ORM类的代码覆盖率非常低。
当时,我可以选择修改代码覆盖率报告,手工地排除掉这些类。但是,我也是程序员,要用程序员的方式来解决这个问题。受到Windbg提高代码覆盖率的启发,我用IronPython Console加载(load)被测试程序集(assembly),实例化ORM类的对象,然后访问其属性。2分钟后,我发现这些类具备相同的模式:拥有无参数的构造函数,除了属性外没有其他成员,所以属性都是基本类型(数值、字符串、日期)。于是,我编写了一个几十行的IronPython程序来自动访问这些类和它们的属性。其核心代码如下:
1: def access_attribute(namespace): 2: for name in dir(namespace): 3: try: 4: type_ = getattr(namespace, name) 5: if type(type_) == type(System): 6: access_attribute(type_) 7: continue 8: 9: bj = type_() 10: for attr in dir(obj): 11: try: setattr(obj, attr, type(getattr(obj, attr))()) 12: except: pass 13: try: setattr(obj, attr, None) 14: except: ass 15: try: setattr(obj, attr, '') 16: except: pass 17: except: 18: pass |
该函数递归遍历名字空间,尝试用无参数构造函数实例化对象,然后访问对象属性。
* 第2行:获取namespace中的元素的名称,并遍历。
* 第4行:获取namespace中的元素,并存放在type_中。在.NET平台上,type_只可能是类或名字空间。
* 第5~7行:如果该元素的类型与System的类型相同,则说明该元素也是名字空间,那么递归遍历该名字空间。
* 第9行:尝试用无参数构造函数实例化type_的对象,并存放在obj中。
* 第10~16行:遍历obj对象的成员,并尝试对其赋值。
* 代码中出现了大量的try语句块,这保证出现异常时,整个遍历过程不会被中断。