测试:
程序的形式推理(验证)-> 代码审核 -> 测试
测试优先编程
尽早并经常进行测试 单个功能开发顺序:
1.编写功能说明
2.编写符合规范的测试
3.编写实际代码,一旦代码通过测试即完成
通过分区选择测试用例
将输入的每个子域由一组输入组成。子域合在一起完全覆盖了输入空间,因此每个输入都位于至少一个子域中
子域的思想:将输入空间划分为程序在其上具有相似行为的相似输入集。
…
在分区中包括边界:
eg: • 0是正数和负数之间的边界
• 数值类型的最大值和最小值,例如int和double
• 集合类型的空性(空字符串,空列表,空数组)
• 集合的第一个和最后一个元素
---------------
覆盖分区的两个极端• 完整的笛卡尔积。 每个测试分区的合法组合都包含一个测试用例。
• 覆盖每个部分。
每个维度的每个部分都至少包含一个测试用例,但不一定是每个组合。
使用JUnit进行自动化的单元检测
黑盒和白盒测试
黑盒测试:仅从规范中选择测试用例,而不从功能的实现中选择。
白盒测试(也称为玻璃盒测试)意味着选择具有实际功能实现知识的测试用例。(例如,如果实现根据输入选择不同的算法,则应根据这些域进行分区。如果该实现保留一个内部缓存来记住先前输入的答案,那么您应该测试重复的输入)测试用例不需要规范未明确要求的特定实现行为
• 语句覆盖率:每个语句是否都由某个测试用例运行?
• 分支覆盖:对于程序中的每个if或while语句,某个测试用例是否同时遵循正确与错误的方向?
• 路径覆盖:某个测试用例是否采用了分支的所有可能组合(程序中的每个路径)
分支覆盖范围比语句覆盖范围要强,路径覆盖范围比分支覆盖范围要强
单元测试:隔离测试单个模块(例如方法或类)
集成测试对模块的组合甚至整个程序进行测试
自动化测试是指运行测试并自动检查其结果
避免调试
消除错误:
不变性(不受更改的影响)是防止错误的另一项设计原则。一个不可变的类型是一个类型,其价值一旦被创造永远无法改变。
本地化错误
尝试将它们本地化到程序的一小部分,这样就不必费劲查找错误的原因
断言(assert)
断言可以记录当时有关程序状态的假设 在Java中,运行时断言是该语言的内置功能 assert x >= 0; 如果x ==
-1,则此断言失败并显示错误消息 Java断言的一个严重问题是断言默认情况下处于关闭状态
您应该断言一些事情:
方法参数要求,如sqrt。 方法返回值要求。 这种断言有时称为自我检查。例如,
assert x >= 0; double r; ... // compute result r assert Math.abs(r*r - x) < .0001; return r; } ``` 涵盖所有情况。如果条件语句或转换未涵盖所有可能的情况,则优良作法是使用断言阻止非法情况: ```switch (vowel) { case 'a': case 'e': case 'i': case 'o': case 'u': return "A"; default: assert false; } ```
切勿使用断言来测试程序外部的条件,例如文件的存在,网络的可用性或人类用户键入的输入的正确性。断言测试程序的内部状态,以确保它在其规范的范围内。断言失败时,表明该程序在某种意义上已经脱离轨道,进入了无法正常运行的状态。因此,断言失败表明存在错误。外部故障不是bug,您无法对程序进行任何预先更改以防止它们发生。外部故障应使用异常处理。
模块化: 模块化意味着将系统划分为组件或模块,每个组件或模块都可以与系统其余部分分开设计,实施,测试,推理和重用。
由一个非常长的main()函数组成的程序是整体的一个模块会很难理解,也很难隔离错误。所以相比之下,分解为小函数和类的程序则更具模块化。封装:封装是指在模块周围建造墙,以使模块负责其自身的内部行为,并且系统其他部分中的错误不会破坏其完整性。
一种封装是访问控制,
使用public和private控制变量和方法的可见性和可访问性。任何代码都可以访问公共变量或方法(假设包含该变量或方法的类也是公共的)。私有变量或方法只能由同一类中的代码访问。尽可能地保持私有状态,尤其是对于变量,可以提供封装,因为它限制了可能无意中导致错误的代码。
> 另一种封装来自可变范围。
从表达式和语句可以引用该变量的意义上来说,变量的范围是程序文本中定义该变量的部分。方法参数的范围是方法的主体。局部变量的范围从其声明扩展到下一个右花括号。保持变量作用域尽可能小,可以更容易地推断出程序中可能存在错误的位置。例如,假设您有一个像这样的循环:
for (i = 0; i < 100; ++i) {
…
doSomeThings();
… }
代码评审
两个主要目的:
o 提升代码质量 o 提升程序员的水平
代码风格因人而异,每个公司也都会有自己的标准
o 不要重复你的代码(DRY)
o 仅在需要的地方做注释
o 快速失败/报错
o 避免使用幻数
o 一个变量有且仅有一个目的
o 使用好的命名
o 避免使用全局变量
o 返回结果而非打印它
o 使用空白符提升可读性
规格说明
一种 规格 方法的组成由几个子句组成:
• 一种 前提,由关键字require指示
前提条件是对客户端(即方法的调用者)的义务。这是调用该方法的状态的条件
• 一种 后置条件,由关键字effects指示
后置条件是该方法的实现者的义务
良好的设计规格
• 规范的形式:简洁,清晰,结构合理,以便于阅读
• 规范应该连贯
• 连接结果应该有效益
• 规格应足够坚固,在完善顾客的需求的时候,不会轻易将之前的都被破坏掉
• 规格也应足够弱:比如再打开文件时,应给它不能打开文件而报错返回的时候,不能只是强制打开
• 在可能的情况下应使用抽象类型