您的位置:软件测试 > 开源软件测试 > 开源单元测试工具 >
追求代码质量: 用AOP进行防御性编程
作者:网络转载 发布时间:[ 2013/2/25 14:10:13 ] 推荐标签:

开发人员测试的主要缺点是:绝大部分测试都是在理想的场景中进行的。在这些情况下并不会出现缺陷 —— 能导致出现问题的往往是那些边界情况。

什么是边界情况呢?比方说,把 null 值传入一个并未编写如何处理 null 值的方法中,这是一种边界情况。大多数开发人员通常都不能成功测试这样的场景,因为这没多大意义。但不管有没有意义,发生了这样的情况,会抛出一个 NullPointerException,然后整个程序会崩溃。

本月,我将为您推荐一种多层面的方法,来处理代码中那些不易预料的缺陷。尝试为应用程序整合进防御性编程、契约式设计和一种叫做 OVal 的易用的通用验证框架。

清单 1 中的代码为给定的 Class 对象(省去了 java.lang.Object,因为所有对象都终由它扩展)构建一个类层次。但如果仔细看的话,您会注意到一个有待发现的潜在缺陷,即该方法对对象值所做的假设。

    
public static Hierarchy buildHierarchy(Class clzz){

 Hierarchy hier = new Hierarchy();
 hier.setBaseClass(clzz);
 Class superclass = clzz.getSuperclass();

 if(superclass != null && superclass.getName().equals("java.lang.Object")){
  return hier;
 }else{     
  while((clzz.getSuperclass() != null) &&
    (!clzz.getSuperclass().getName().equals("java.lang.Object"))){
     clzz = clzz.getSuperclass();
     hier.addClass(clzz);
  }        
  return hier;
 }
}    


刚编好这个方法,我还没注意到这个缺陷,但由于我狂热地崇拜开发人员测试,于是我编写了一个使用 TestNG 的常规测试。而且,我还利用了 TestNG 方便的 DataProvider 特性,借助该特性,我创建了一个通用的测试用例并通过另一个方法来改变它的参数。运行清单 2 中定义的测试用例会产生两个通过结果!一切都运转良好,不是吗?

清单 2. 验证两个值的 TestNG 测试

    
import java.util.Vector;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class BuildHierarchyTest {
 
 @DataProvider(name = "class-hierarchies")
 public Object[][] dataValues(){
  return new Object[][]{
   {Vector.class, new String[] {"java.util.AbstractList",
      "java.util.AbstractCollection"}},
   {String.class, new String[] {}}
  };
 }

 @Test(dataProvider = "class-hierarchies"})
 public void verifyHierarchies(Class clzz, String[] names) throws Exception{
  Hierarchy hier = HierarchyBuilder.buildHierarchy(clzz);
  assertEquals(hier.getHierarchyClassNames(), names, "values were not equal");
 }
}


至此,我还是没有发现缺陷,但一些代码问题却困扰着我。如果有人不经意地为 Class 参数传入一个 null 值会怎么样呢?清单 1 中第 4 行的 clzz.getSuperclass() 调用会抛出一个 NullPointerException,是这样吗?

测试我的理论很容易;甚至都不用从头开始。仅仅把 {null, null} 添加到初始 BuildHierarchyTest 的 dataValues 方法中的多维 Object 数组中,然后再次运行它。我定会得到如图 1 所示的 NullPointerException:

图 1. 可怕的 NullPointerException

防御性编程

一旦出现这个问题,下一步是要拿出对抗的策略。问题是我控制不了这个方法能否接收这种输入。对于这类问题,开发人员通常会使用防御性编程技术,该技术专门用来在发生摧毁性后果前捕捉潜在错误。

对象验证是处理不确定性的一项经典的防御性编程策略。相应地,我会添加一项检验来验证 clzz 是否为 null,如清单 3 所示。如果其值终为 null,我会抛出一个 RuntimeException 来警告他人注意这个潜在问题。

清单 3. 添加验证 null 值的检验

    
public static Hierarchy buildHierarchy(Class clzz){
 
 if(clzz == null){
  throw new RuntimeException("Class parameter can not be null");
 }

 Hierarchy hier = new Hierarchy();
 hier.setBaseClass(clzz);

 Class superclass = clzz.getSuperclass();

 if(superclass != null && superclass.getName().equals("java.lang.Object")){
  return hier;
 }else{     
  while((clzz.getSuperclass() != null) &&
    (!clzz.getSuperclass().getName().equals("java.lang.Object"))){
     clzz = clzz.getSuperclass();
     hier.addClass(clzz);
  }        
  return hier;
 }
}    

上一页1234下一页
软件测试工具 | 联系我们 | 投诉建议 | 诚聘英才 | 申请使用列表 | 网站地图
沪ICP备07036474 2003-2017 版权所有 上海泽众软件科技有限公司 Shanghai ZeZhong Software Co.,Ltd