开发者的干爹——读《现代软件工程——构建之法》第二章测试有感

开发者的干爹

读《现代软件工程——构建之法》第二章有感

  上回说到,较之所谓“多门技术一把抓”的大牛,“编程思维”更加重要。如果编程者是艺术家,那么“思维”就好比是艺术家的灵魂。

  然而仅仅凭借“思维”,开发者就能将“开发”的瞬间完成吗?孟子云“挟泰山以超北海,诚不能也”,就好比是,牛皮不是吹的,泰山不是堆的,火车更不是用手推的。哲学中讲,物质依赖于过程而运动,因运动而存在。而在软件开发中,这种过程就是开发流程。

一、被忽视的牛人

  “看似很普通,其实很厉害”是许多“牛人”的通性,单元测试就是这么个“狠角色”。根据不同时期的任务划分,软件的生命周期可分为:软件定义,软件设计,软件开发和单元测试,集成测试,部署以及调试等6个阶段1

  从理论上讲,测试的地位很特殊。其特殊表现在,其功能是破坏性的。其目的旨在证明程序的“不健全性”。

  测试包括:单元测试和集成测试,而单元测试却常常被归入到“软件的实现”当中1。单元测试与集成测试具有相同和不同之处,相同之处表现为,二者都是为了“找茬”,而单元测试更偏向于将功能能够进一步推进。

  在需要“合作”的实际项目中,很多错误来源于自身对模块功能的误解、疏忽以及对于模块变化的不了解。正因为“单元测试”的存在,模块功能定义更明确、对其他模块的影响更少,从而保证了模块的质量。

 二、敢问英雄大名

  1.定义:“单元测试”是程序针对某一功能区所进行的验证其自身不合理性的过程2

  2.目的:找出功能部分的不合理性2

  3.步骤2

    A.设置数据——对功能部分进行测试。

    B.对目标功能进行测试

    C.比较实际、预期结果的差别

三、眼里不揉沙子

  1.测试不是为了证明程序是对的,而是尽量证明程序是错的1

  2.其目的1

    A.测试是为了发现程序中的错误而执行程序的过程

    B.好的测试方案极可能是发现迄今为止尚未发现的错误的测试方案

    C.成功的测试是发现至今为止尚未发现的错误测试。

四、我是有组织的

  知识从来都不会单独存在,往往都是通过一个个的节点,将看似分散的知识片段进行整合。

  测试也是一样,它具有自己的组织体系,根据张海藩、吕云翔的《软件工程(第四版)》中所述,根据测试方式的不同,测试分为如下2个部分:

  1.黑盒测试

    不考虑程序内部的数据结构和处理过程,其只在接口进行测试,检查程序功能是否按照规格说明书的正常使用,是否能正确接受输入数据,产生输出信息,并且保持外部信息的完整性(包括:等价类划分和边界值分析)1

  2.白盒测试

    完全了解程序的结构和处理过程。此方法按照程序内部的逻辑测试程序。

    白盒测试还包括:逻辑覆盖(语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖以及条件组合覆盖)以及控制结构测试(基本路径测试、条件测试以及数据流测试)1。 

五、作为大哥的标准

  第一点曾经说过,单元测试更偏向于推动开发过程的向前发展。这就像是一个带头大哥,然而“大哥不是你想当,相当就能当”,这需要一个标准2,如下:

  1.单元测试应该在最基本的功能/参数上验证程序的正确性。

    A.从面向对象的设计原理出发,最基本的功能点由一个类及方法表现。

    B.单元测试要测试API的每一个方法和参数。

  2.单元测试需要由最熟悉代码的开发者书写  谁写的代码谁清楚,心里还没点数吗?

  3.单元测试后,机器状态不变

    “城头变幻大王旗”,为了使得测试充分,单元测试当然不会只执行一次,而是会有很多次,需要将各次的测试实例之间不互相干扰。

  4.单元测试要快 只有当测试速度变快,才能从直观上,看出程序效率。

  5.单元测试应该产生可重复、一致的结果。然而不要希望测试能够发现所有问题。

  6.独立性——状态不依赖于别的测试

  7.单元测试应该覆盖所有代码路径,此处联想到白盒测试中的基本路径测试的相关内容。为保证代码覆盖率,需以公开和私有的函数/方法开刀。其中,100%的代码覆盖率不等于100%的正确性。

    A.代码覆盖率对于“应写而未写”的代码无能为力。

    B.代码中的效能问题

    C.多线程环境中的同步问题,这个问题和代码的时序、共享资源锁定有关。

    D.“覆盖率”的层次:函数、语句、分支、条件

  8.单元测试应该集成到自动测试的框架中

  9.单元测试必须和产品代码一起保存和维护

六、回归测试很有用(回归到以前不正常的状态)

  程序永远开发和测试不会终结,都是一个里程碑性质不断迭代和推进,当下程序对吗?好用吗?整合了新模块以后呢?为了使程序在不断补充功能以后都强劲,需进行回归测试。其主要目的有2:

  1.验证代码的确改正了缺陷

  2.同时证明新代码未破坏现有功能

  除此之外,“回归”二字应理解为“回归以前不正常状态”,最好自动化,如此可以对每个构建快速运行的回归测试,然而如何将测试进行现实性的操作呢?

  分析的主要方法有2种:1.抽样2.代码注入2,其中包含顺序为:先用抽样方法找到目标瓶颈所在,后用代码注入方式将检测的代码注入到每一个函数中。

  实例:   调用频繁会造成程序的开销很大。

      for(int i=0;i<list.size();i++)此句话会占用非常多的资源。

      但是如果将list.size()移除循环,则会将资源占用大幅降低;由此说明,若我们不经分析就盲目优化,未必会效果好。

七、你自己的活儿(个人开发流程)

  无论是团队还是机体都会面临相似的问题,如何开发?如何有效?如果闭目造车,想当然,我想漏洞很多,效率很差,这些都是不可取的。

  未解决这个问题,这里引入了PSP阶段概念,即个人开发流程,这作为个人进行开发的主要指导,从而将理论与实际相结合,

  PSP阶段2

    1.计划  明确需求和其他相关因素,估计每个阶段的时间成本

    2.开发

      a) 需求分析  b) 生成设计文档  c) 设计复审 d) 代码规范  e) 具体设计  f) 具体编码  g) 代码复审  h) 测试(自测、修改代码、提交修改)

    3.报告

      a)计算工作量 b)事后总结 c)提出过程改进计划

  其独具匠心的特点2

    1.不局限于某一技术

    2.不依赖于考试

    3.在小型、初创团队,质量不高不能全部归咎于程序员。

    4.依赖于数据

    5.目的是记录工程师如何实现需求的效率而非满意度。

    

八、实践中的“发光发热”

  测试的基本准则是“可以扩展,不可修改”,对于扩展而言,我们主要分别从1.数据 2.需求 3.用户  4.软件构建 等四个层次进行扩展2。然而如果需要“有的放矢”的话,我们会将精力投放于对于“功能需求”的细分层次上,可以将功能需求从3个层次进行划分,即:1.基本功能 2.扩展功能 3.高级功能。

  与单元测试偏向“推动开发”的特点不同,回归测试是为了找错从而能够将测试的自动化程度不断提2,基本要求为:

    1.手动测试,手工比较 

    2.不断测试

    3.把测试文件、正确测试结果保存文件中,测试驱动只要比较测试的输出和标准结果就能得到答案。

    4.再进一步,把自动构建脚本和构建验证测试结合起来。每次构件后,自动测试记录Bug

  综上所述,对第二章内容主要是“单元测试”的必要性和优越性,做一个比较全面的概述和感悟。

参考文献

1张海藩 吕云翔 《软件工程(第四版)》 2013.08.01 人民邮电出版社

2邹欣 《现代软件工程-构建之法(第三版)》 2017.03 人民邮电出版社

原文地址:https://www.cnblogs.com/LiYuxuan1104/p/Chapter02.html