【软件构造】软件测试

  测试是确保程序正确性/健壮性的最普遍的手段。它关注软件的某一侧面的质量特性,用来确认软件是否达到可用水平,也是提高软件质量的重要手段.
  测试是软件开发过程中容易被轻视却又极其重要的一个环节。《人月神话》中提到的进度安排法则中给编码分配了1/6的时间,而给调试和测试分配了总进度一半的时间,这是因为尽管大多数项目开始时不会为测试那么多的时间,但他们最终花费的实际时间却常会达到进度的一半。
  进行软件测试需要用等价划分和边界值分析方法设计测试用例,尽可能全的对测试代码进行覆盖测试。但即便是最高水平的测试也无法做到100%的正确,再好的测试也不能证明一个系统是完全正确的。实际上,一般的工业软件也只能保证1‰-10‰的残留残缺率

调试与各类测试的解释

  • 测试:测试人员对开发人员提交的代码进行的”破坏性“工作。设计覆盖率尽可能高的测试用例集合,从代码中找出尽可能多的问题
  • 调试:开发人员在开发过程中进行的”建设性“工作,帮助开发者定位和解决程序中较为直观的问题,让代码在一般情况下可以正常运行

  • 白盒测试:对程序内部代码结构的测试
  • 黑盒测试:对程序外部表现出来行为的测试

  • 静态测试:不运行代码,类似校对,依靠编译器等工具检查语法和数据流等
  • 动态测试:运行代码,在调试器环境下执行程序

  • 单元测试:验证特定代码部分的功能,通常是功能层面的
  • 集成测试:由多个程序员编写的多个类、包、组件、子系统的组合测试
  • 系统测试:测试完全集成系统,在最终配置中执行软件
  • 回归测试:修改旧代码后重新进行的测试,确保修改没有引入新的bug
  • 验收测试:又叫做验收测试,是部署软件之前的最后一个测试行为,确保软件准备就绪

测试用例

  测试用例:输入+执行条件+期望结果
  依靠穷举设计测试用例来进行测试是不可能的,不能用调试的思路来进行测试。良好的测试用例有以下特点:

  1. 最可能发现错误
  2. 不重复,不冗余
  3. 在同等价类中是最有效的
  4. 不过于简单也不过于复杂

测试优先编程与测试驱动开发

  测试优先编程的过程是写方法规范(specification)=>写测试用例=>写代码。
  这一方法最大的优点是可以在写测试用例的过程中理解、修正、完善方法规范。在编写好测试用例之前,我们对于最终系统的应用场景是比较抽象的,所以写出的方法规范也很有可能有缺陷、模糊的部分甚至错误存在。如果我们完成方法规范的编写之后立即开始编码,这些问题很有可能保留到编码结束的测试阶段,因为编码参照写出的方法规范即可,并不会像测试那样探索在更具体场景中的系统。
  优先编写测试用例也为我们的编码工作做了一些思路的铺垫,可以在一定程度上减少在调试程序和返工上的时间投入。
  将测试优先编程的思想拓展到更大的项目上就获得了测试驱动开发(TDD)。这种开发形式会让具体的测试用例来承担需求的角色。

JUnit自动单元测试

  JUnit是一个被广泛使用的java单元测试框架,对于测试驱动开发非常重要。
  下面演示在IDEA使用Junit的方法

  1. 在官网下载junit以及hamcrest-core的jar文件。我最初下载使用的版本是5.8.1,但后续发现有很多意外的问题出现,就还是改用旧的Junit4。(hamcrest-core1.3和Junit4.13.2)
  2. 打开IDEA,创建新项目,选择文件=>设置=>插件,安装junit插件
    image
  3. 选择文件=>项目结构=>库=>+=>Java将下载的junit导入。
    image
  4. 在src文件夹下创建新库,命名为test
  5. 选择文件=>项目结构=>模块=>源,将库test打上测试标记
    image
  6. 在上一步的界面点击依赖,选中junit并应用
    image
  7. 之后选中需要测试的类名,使用快捷键alt+enter创建测试即可
    image

  用这个类做一个小测试

public class Tester
{
    public float plus(float x,float y)
    {
        return x+y;
    }
    public float minus(float x,float y)
    {
        return x-y;
    }
}

  生成测试类并编写简单的测试代码。当然这只是对Junit使用的的一次简单尝试,正式的测试是不能这么随便的。
  其中减法的测试故意写上了错误的输出。点击类或者方法左侧的绿色三角开始测试(注意不要用右上角的运行和调试,它们都是以main函数为入口的)
image
  测试结果如下:故意写错的minus被测试找了出来。
image

如何写测试用例

  测试用例=输入+执行条件+期望结果。测试最关键的内容就是设计测试用例。
  依靠穷举设计测试用例来进行测试是不可能的,不能用调试的思路来进行测试。良好的测试用例有以下特点:

  1. 最可能发现错误
  2. 不重复,不冗余
  3. 在同等价类中是最有效的
  4. 不过于简单也不过于复杂、

  要设计一套优质的测试,我们需要

  1. 在测试类中写下自己的测试策略
  2. 划分测试方面的等价类
  3. 考虑各类之间的边界类
  4. 根据等价类和边界类设计测试用例

测试策略

  概括后三步的所有内容,与后三步并行。测试策略简单来说就是“根据什么选择的测试用例”;这段文本需要包含测试代码的大体思路、测试覆盖了哪些等价类和边界类等内容,以注释形式写在测试类的最前面。
  编写测试策略主要目的是在代码评审过程中,让其他人更好更快地理解你的测试,以便作出更快更准确的评价。

划分等价类

  针对被测函数的输入要满足的约束条件,将被测函数划分为等价类,从等价类中导出测试用例。
  划分出的等价类既包括满足约束的有效数据集合,又包括不满足约束的无效数据集合。前者检测的是程序的正确性:能否得到正确的输出;而后者检测程序的健壮性:获得不满足约束的输入后能否向用户正确反馈问题,能否合理处理异常。
  正确地划分好等价类后,等价类中的每一种输入都应该能够单独让程序呈现出相同正确性和健壮性的输出,因此每个等价类中只需要取一个测试用例,这样能有效降低测试用例数量

考虑边界类

  在我们划分好等价类后,可能会有一些输入存在某些等价类的边界处,且不属于任何一个在上一步划分出来的等价类。我们把这些输入的类型叫做边界类。
  相对于等价类,一种边界类中包含的元素可能极少,甚至一个边界类中可能只存在一个具体的输入,一般来说都可以叫做“特殊情况”。比如大于0和小于0的数字的输入被分为两个等价类,那输入0就可以看作一个含单独元素的边界类。
  一般来说,边界类涵盖的具体输入较少,但程序往往却更容易在获得边界类的输入时出问题。所以我们也要将边界类当作等价类来设计测试用例。

根据等价类和边界类设计测试用例

  设计具体的测试用例有两种思路。
  一是多设计涵盖多个等价类和边界类单个测试用例,只要保证划分出等价类和边界类都至少在某个测试用例中出现过即可。这种设计思路需要更少的测试用例,成本较低,但是未必有较高的覆盖度
  二是做所有等价类边界类的笛卡尔积,为一个组合都设计一个测试用例。这样的设计能够做到真正的全覆盖,但是需要极大量的测试用例,成本较高
  以上两种思路可以看作测试用例设计的两个极端,在实际的工程中,往往会根据情况选择介于两者之间的测试思路

posted @ 2023-03-01 21:07  NoSLoofah  阅读(80)  评论(0)    收藏  举报