4、结构化系统实现
4、结构化系统实现
编码
目的
把模块的过程性描述翻译为用选定的程序设计语言书写的源程序(源代码)。
依据
编码的主要依据是概要设计和详细设计说明文档。
任务
理解概要设计和详细设计说明书;
遵循编码原则和风格进行翻译,形成源代码。
三类程序设计语言
机器语言
纯二进制串,计算机直接识别,效率高,但是效率低,重用性差。
汇编语言
跟机器语言一样,但是有助记符。
机器指令:1000100111011000 汇编指令:MOV AX,BX
比机器语言易读写、易调试和修改,执行速度快(因为需要编译成机器码,会比机器码稍慢)、占内存少,针对硬件编制。
不能编写复杂程序,依赖于机型、不通用、不可移植。
高级语言
与自然语言相近,是面向用户的语言。
编码效率高,通用性强,兼容性好,便于移植。
运行效率低,对硬件操作不如汇编。
语言选择标准
系统用户要求
如果开发系统由用户维护,通常要求用用户熟悉的语言书写。
可以使用的编译程序
运行目标系统环境可提供编译程序限制可选用语言的范围。
可以得到的软件工具
有支持程序开发的软件工具可以利用。
工程规模
规模庞大,现有语言不适用,设计实现供该工程项目使用程序设计语言。
程序员的知识
如果和其他标准不矛盾,应选择程序员熟悉的语言。
软件可移植性要求
若目标系统在不同计算机上运行,选择可移植性好的语言。
软件的应用领域
选择语言时应充分考虑目标系统的应用范围。
编码风格
逻辑简明清晰、易读易懂是重要标准。
原则
按程序内部的文档,标准的数据说明,语句构造简单,输入输出的检查, 提升优化效率和存储容量。
程序内部的文档
使用恰当的描述符
含义鲜明,标识符看一眼就知道是干嘛的;命名规则一致;避免过长或过短;缩写规则一致;函数用大写字母开头。
常用的缩写:tmp
,msg
等
适当注释
合适的注释量应该在20%以上。
序言性注解
模块开始,描述模块功能、主要算法、接口特点、重要数据及开发简史(设计者、复审者及时间、修改日期)。
中间注解
插在程序中间,解释这段代码的必要性及功能。
良好的视觉组织:空格、空行、缩进
代码块内部要根据层次缩进,空格使用要恰当。
数据说明
数据说明次序应标准化(按数据结构或数据类型说明);
多个变量名在一个语句说明,按字母顺序排列;
复杂数据结构用注解说明实现方法和特点。
语句构造
避免把多个语句写在同一行;
尽量避免复杂条件测试
尽量减少“非”条件测试
避免大量使用循环嵌套和条件嵌套;
利用括号使表达式运算次序清晰直观;
尽量少用goto;
if、for、do、while、case、switch、default 等语句占一行,且if 、for 、do 、while 等语句的执行语句部分无论多少都要加括号{};
不将BOOL值TRUE和FALSE对应1和0编程;
IO
对所有输入数据都进行检验,保证输入有效;
检查输入项重要组合合法性;
保持输入格式简单;
使用数据结束标记,不要求用户指定数据数目;
提示交互式输入请求,如可用选择或边界数值;
程序设计语言对格式有严格要求时,应保持输入格式一致;
设计良好输出报表;
给所有输出数据加标志。
效率和存储容量
程序运行时间
简化算术和逻辑表达式;
嵌套循环,确定是否有语句可从内层往外移;
尽量避免使用多维数组;
尽量避免使用指针和复杂的表;
使用执行时间短的算术运算;
不要混合使用不同的数据类型;
尽量使用整数运算和布尔表达式。
存储器效率
大中型计算机考虑操作系统页式调度特点,将程序功能合理分块,每个模块或一组密切相关程序体积与每页容量相匹配,减少页面调度。
微型计算机关键是程序简单性,选择生成较短目标代码且存储压缩性能优良的编译程序。
IO效率
所有输入/输出都应有缓冲,减少通信的额外开销;
对二级存储器(如磁盘)选用最简单访问方法;
辅助存储器的输入/输出以信息组为单位进行;
如“超高效”输入/输出很难被理解不采用。
软件测试基础
软件测试目标
测试是为了发现程序中的错误而执行程序的过程;
好的测试方案是极有可能发现迄今尚未发现的尽可能多的错误的测试;
成功的测试是发现了迄今尚未发现的错误的测试。
黑盒和白盒测试
黑盒测试
如果知道产品应具有功能,可通过测试来检验是否每个功能都能正常使用(从用户角度测试)。
白盒测试
如果知道产品内部工作过程可通过测试来检验产品内部动作是否按照规格说明书的规定正常进行。
测试准则
所有测试应能追溯到用户需求,测试的目的是发现错误,其中最严重的是不能满足用户需求的错误;
应尽早地和不断地进行软件测试;
充分注意测试中群集现象(Pareto原理);
Pareto原理
80% 的错误可以通过修复导致错误的问题中的 20% 来解决。
因此,与其处理每个出现的错误,不如努力找出导致最多问题的错误。当你发现这些错误时,修复它们,然后你的大部分(大约 80%)的问题都会得到解决。
测试应从小规模开始,逐步进行大规模测试;
一般不能做到穷举测试;用第三方测试原则。
逻辑覆盖
逻辑覆盖,就是用尽量少的测试样例覆盖程序的全部功能(设计测试方案)。
语句覆盖
选择测试数据,使被测程序中每个语句至少执行一次。
取
判定覆盖
不仅每个语句至少执行一次,而且每个判定的真假分支至少执行一次。
再取
条件覆盖
每个语句至少执行一次,判定表达式每个条件取各种可能结果(每个小判断条件都至少分别取一次真假)。
条件覆盖不一定能包含判定覆盖。
判定/条件覆盖
同时满足判定和条件覆盖的要求:
测试用例 | 条件取值 |
---|---|
(2, 0, 4) | |
(1, 1, 1) |
有时判定/条件覆盖不比判定覆盖更强。
条件组合覆盖
覆盖所有条件的所有真假排列组合:
① | ② |
---|---|
③ | ④ |
⑤ | ⑥ |
⑦ | ⑧ |
测试用例 | 覆盖条件 | 覆盖组合 |
---|---|---|
(2, 0, 4) | ①,⑤ | |
(2, 1, 1) | ②,⑥ | |
(1, 0, 3) | ③,⑦ | |
(1, 1, 1) | ④,⑧ |
控制结构测试
基本路径测试
测试流程
① 根据过程设计结果画出相应流图
画流图就按前边说的方法画。
② 计算流图的环形复杂度
这一步也按前边说的来。
图中
③ 确定线性独立路径的基本集合
独立路径:至少包含一条在定义该路径之前不曾用过的边。
环形复杂度为独立路径基本集的上界。比如这张图中环形复杂度
找出5条独立路径(括号里是新路径):
路径1 | 1-2-4-6-7( |
---|---|
路径2 | 1-2-4-2-5-7( |
路径3 | 1-2-5-7( |
路径4 | 1-3-7( |
路径5 | 1-3-5-7( |
④ 设计测试用例覆盖基本集合的路径
设计一组样例覆盖上面找出的独立路径。
例题
计算不超过100个在规定值域内有效数的平均值;同时计算有效数字的综合和个数。
PDL描述
画流图
算环形复杂度
找独立路径、设计用例
省略,考前练练。
注意
有些独立路径如果不能独立测试,可以放在其他路径里一起测。
循环测试
分类
简单循环
零次循环
从循环入口直接跳到循环出口(循环执行0次)。
一次循环
只让循环执行一次,为了查找循环初始值方面的错误。
二次循环
让循环执行2次。检查在多次循环时才能暴露的错误。
m次循环
执行中间环次数(
最大次数循环、比最大次数多一次循环、比最大次数少一次的循环
最后测试这三种情况会不会有问题。
例
// 求最小值
k = i; // 循环从i开始,到n结束
for(j=i+1; j<=n; j++)
if(A[j] < a[k])
then k = j;
测试用例:
循环 | i | n | A[i] | A[i+1] | A[i+2] | k期望 |
---|---|---|---|---|---|---|
0次 | 1 | 1 | i | |||
1次 | 1 | 2 | 1 | 2 | i | |
1次 | 1 | 2 | 2 | 1 | i+1 | |
2次 | 1 | 3 | 1 | 2 | 3 | i |
2次 | 1 | 3 | 2 | 3 | 1 | i+2 |
2次 | 1 | 3 | 3 | 2 | 1 | i+2 |
2次 | 1 | 3 | 3 | 1 | 2 | i+1 |
嵌套循环
带嵌套时复杂度指数级增加,不能再用经典方法测。
①从最内层循环开始,置所有其它层循环为最小值
② 对最内层循环做简单循环的全部测试。
③ 逐步外推,测试时保持所有外层循环变量取最小值,其它嵌套内层循环变量取**“典型”值**。
④ 反复进行,直到所有各层循环测试完毕。
连锁循环
若各个循环互相独立,可用与简单循环相同方法进行测试。
若几个循环不是互相独立,需要使用测试嵌套循环。
非结构化循环
这种抽象玩意没法直接测试,使用结构化程序设计方法重新设计再测试。
黑盒测试技术
概述
着重测的是软件的功能。应该能发现如下错误:
(1)功能不正确或遗漏;
(2)界面错误;
(3)数据结构或外部数据库访问错误;
(4)性能错误;
(5)初始化或终止错误。
常见的测试技术有等价类划分,边界值分析,错误推测等。
等价类划分
概述
把程序的输入域划分成若干数据类,从每一数据类选取少数有代表性数据做为测试用例。
在各数据类中,各输入数据对揭露程序中的错误等效(随便拿哪个测结果都一样)。
测试过程分为以下步骤:
划分等价类
有效、无效的等价类
有效等价类:合理,有意义输入数据构成集合。
无效等价类:不合理,无意义输入数据构成的集合。
这两个等价类都需要分出来。
划分原则
① 按输入条件规定的范围,定义一有效等价类和两无效等价类。
② 如果输入条件是布尔量,那就只有一个有效等价类和一个无效等价类。
③ 如果规定输入数据一组值,程序对每个输入值分别进行处理。每个输入值确立一有效等价类,针对这组值确立一个无效等价类。
比如:教工分房方案中,按教授、副教授、讲师、助教分别计分。
有效类4个;除了上述四种人,其他统一算无效类1个。
④ 如果规定输入数据必须遵守规则,定义一有效等价类(符合规则)和若干无效等价类(按从不同角度违反规则划分无效等价类)。
比如Pascal语言规定“一个语句必须以分号‘;’结束”。 这时,可以确定一个有效等价类“以‘;’结束”,若干个无效等价类:以‘:’结束、以‘,’结束等。
⑤ 已划分等价类中各元素在程序中处理方式不同,将等价类进一步划分更小等价类。
比如对月份的处理,28、29、30、31四个小等价类。
确立测试用例
建立等价类表,列出所有划分出等价类:
输入条件 | 有效等价类 | 无效等价类 |
---|---|---|
… | … | … |
为每一等价类规定一唯一编号;
设计一新测试用例,尽可能多覆盖尚未被覆盖有效等价类(防冗余,提高效率),重复,直到所有有效等价类被覆盖;
设计一新测试用例,仅覆盖一尚未被覆盖无效等价类,重复,直到所有无效等价类被覆盖。
例
某城市的电话号码由三部分组成:
地区码可空白或三位数字;前缀为非0或1开头的四位数字;后缀四位数字。
等价类表:
输入条件 | 有效等价类 | 无效等价类 |
---|---|---|
地区码 | 空白(1) 3位数字(2) | 含非数字字符(5) 少于三位数(6) 多于三位数(7) |
前缀 | 2000-9999的四位数(3) | 含非数字字符(8) 起始为0(9) 起始为1(10) 少于四位数(11) 多于四位数(12) |
后缀 | 四位数(4) | 含非数字字符(13) 少于四位数(14) 多于四位数(15) |
测试数据:
测试数据 | 覆盖等价类(测试范围) | 期望结果 |
---|---|---|
( ) 2761-2345 | (1) (3) (4) | ✔️ |
(635) 8051-9321 | (2) (3) (4) | ✔️ |
测试数据 | 覆盖等价类(测试范围) | 期望结果 |
---|---|---|
(20A) 1231-4567 | (5) | ❌ |
(33) 2314-5678 | (6) | ❌ |
(1827) 8372-4937 | (7) | ❌ |
... | ... | ❌ |
... | (15) | ❌ |
边界值分析
概述
是对等价类划分方法的补充。确定出边界,找边界上和边界附近的极端情况做测试。
常见的边界值
对16-bit 的整数而言 32767 和 -32768是边界;
屏幕上光标在最左上、最右下位置;
报表的第一行和最后一行;
数组元素的第一个和最后一个;
循环的第0次、第1次和倒数第 2 次、最后一次。
边界选择原则
① 如果输入条件规定了取值范围,则以该范围作为边界;
② 如果输入条件规定值的个数,则以个数为边界;
③ 针对规格说明的每个输出条件,使用原则①和②(如果输出或者别的什么地方也有边界,也要按①和②测出来);
④ 如果规格说明给出的输入或输出域是有序集合(如有序表、顺序文件等),则选取集合中特定次序的元素作为边界,如第一个、最后一个元素等;
⑤ 如果程序中使用了一个内部数据结构,则应选择该结构的边界上的值,如数组、链表等(带有范围容易越界的东西);
⑥ 分析规格说明,找出其它可能边界条件。
例
某报表处理系统要求用户输入处理报表日期,日期在2001年1月至2005年12月,由年、月6位数字字符组成,前四位代表年,后两位代表月。用边界值分析法写出测试用例。
分析表:
输入条件 | 测试用例 | 测试数据 | 期望结果 | 选取理由 |
---|---|---|---|---|
类型和长度 | 1个数字 5个数字 7个数字 1个非数字 全非数字 6个数字 | 5 20035 2003005 2003.5 MAY 200305 | ❌ ❌ ❌ ❌ ❌ ✔️ | 略 |
日期范围 | 在边界选取 | 200101 200512 200012 200601 | ✔️ ✔️ ❌ ❌ | 略 |
月份范围 | 1月 12月 <1 >12 | 200301 200312 200300 200313 | ✔️ ✔️ ❌ ❌ | 略 |
错误推测
这种方法其实就是靠经验找错,你觉得哪可能会错。
靠经验和直觉推测程序可能存在错误,要有针对性地编写检查这些错误的测试用例。
常见错误:
输入数据或输出数据为零;
输入或输出数目为零(如表空或只1项) ;
缺省值;
空白;
空值;
多个数据的组合效应;
错误的群集现象(如果找到错了,那就极有可能有别的错);