代码测试篇

代码测试篇

  • 测试人员应该具备的代码级测试基础知识:代码级测试技术入门、方法论、用例设计、覆盖率衡量、典型难点、解决思路等
  • 代码级错误
    • 有特征错误:语法特征错误、边界行为错误、经验特征错误
    • 无特征错误:算法错误、部分算法错误

常见代码错误类型

  1. 语法特征错误
  • 从语法上就能发现的错误
  • e.g. 数组越界
  1. 边界行为特征错误
  • 在执行过程中发生异常、崩溃或超时
  • e.g. a/b b=0时就会发生边界错误
  1. 经验特征错误
  • 根据过往经验发生的错误
  • e.g. if(a=b) 本来想做逻辑比较,结果写成了赋值
  1. 算法错误
  • 代码的预期计算结果与实际结果不一致
  1. 部分算法错误
  • 只在特定的输入情况下,算法不能准确的完成业务功能

代码测试常用的方法

静态方法

  • 不用实际执行代码而发现的代码缺陷
  1. 人工静态方法
  • 开发人员代码走查:开发自己查自己的代码
  • 结对编程:敏捷软件开发下的方法,两个人一起,一个“驾驶员”,一个“观察员”,通常会定期更换两个角色
  • 同行评审:提交的代码在merge到master之前,必须由同级别或更高阶别的同事review之后才能merge,比如Github里提交PR,PR被统一才能merge
  1. 自动静态方法
  • 通过词法分析、语法分析、控制流分析等技术,并结合预定义和自定义代码规则,对程序进行静态扫描发现的语法错误、潜在语义错误以及部分动态错误一种代码分析错误
  • 工具:Sonar、Converity都支持多种语言

动态方法

  • 通过实际执行代码发现的代码缺陷
  1. 人工动态方法
  • 主要的测试手段
  • 通过输入、执行代码、测试输出
  • 善于发现算法错误和部分算法错误
  1. 自动静态方法
  • 又称自动边界测试方法
  • 基于代码自动生成边界测试用例并执行,以捕获潜在的异常、崩溃和超时

动态测试方法

单元测试的3个主要难点

  1. 输入参数的复杂性
  • 被测函数的输入参数
    1
    2
    3
    4
    //输入数据:a和b的不同取值以及组合
    int someFunc(int a,int b){
    ...
    }
  • 被测函数内部需要读取全局静态变量(包括类成员变量)
  • 那么这个变量也是被测函数的输入参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //输入数据:a、b、someGlocalVariable的不同取值以及组合
    Boolean someGlocalVariable = true;
    void func_SUT(int a){
    if(someGlocalVariable){
    funcA();
    }else{
    funcB();
    }
    }
  • 函数内部调用子函数获得数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //func_SUT()传了a,其实内部只是FuncX用了一下,这种情况属于间接传递参数,一般直接对FuncX(a)打桩
    void func_SUT(int a){
    Boolean toggle = FuncX(a);
    if(toggle == true){
    funcA();
    }else{
    funcB();
    }
    }
  1. 预期输出的复杂性
  • 被测函数的返回值
    1
    2
    3
    int add(int a,int b){
    return a+b;
    }
  • 被测函数改写了成员变量和全局变量,那么这些变量也要作为assert的对象
  • 被测函数中进行的文件更新、数据库更新、消息队列更新
    • 在实际单元测试实践中,因为测试解耦的需要,一般不会真正做这些操作,而是借助Mock对象的断言来验证是否发起了相关操作
  1. 关联依赖的代码不可用
  • 使用桩函数来代替被依赖的不可用的函数