作者TonyQ (自立而後立人。)
看板Soft_Job
标题[闲聊] 笑谈软体测试的几个阶段(三)
时间Sun Mar 25 21:21:46 2012
前情提要,这是写给软体开发者的测试历程,
主要是以我自己进入测试的经验说明如何进入测试门槛,
以及我身为软体开发者进行软体测试时需要知道的事情跟可能的心路历程。
所有的内容都只是个人经验仅供参考,欢迎提供意见分享跟挑战。
-----------------------------------
另外这里所指的软体测试都只是主要针对者自己对软体的测试,
而与专案测试(目的为增加专案品质)的目的并不直接相关。
也就是说,这里讲的主要目的,是开发者如何可以在开发过程,
快速验证自己的修改是否有误跟常见的一些问题。
-----------------------------------
本文开始
-----------------------------------
我刚开始进入程式领域时,
并不会有组织化的架构,因为连逻辑都写的很吃力。
测试时也会主要针对整个应用去做测试,(上一篇说过,这就是整合测试。)
因为整合测试实际上是以你写完的作品去做实际测试,
所以在开发上是最直觉、最容易建立出测试流程的测试。
(但不见得是最容易测试的路径)
-----------------------------------
最原始的整合测试的方法相当简单,就是把程式整个执行,
然後按照始用者可能会操作的流程去测试预期的结果。
-----------------------------------
之前提到当开始写起复杂的应用程式之後,
测试其中一小片段的测试路径,开始变得很高成本跟困难。
这时候测试路径就会变得很复杂,
举例,假设我写了踩地雷好了,
我要测踩地雷标出所有地雷之後画面显示是不是对的,
难不成我每次修改那个画面时,还要都去玩几轮作测试。Orz
-----------------------------------
但是前面说过,这样的状况下,测试路径实在是常到让人厌烦。
於是我们会在整合测试的环境下,偷偷开始「作弊」,
以踩地雷为例,
我可能会
1.事先印出当局答案来加速测试
2.设定一些方式跳过游戏内容直接进入游戏获胜
3.想办法设计成可以对印出结果独立呈现。
或者是我设计成使用者只要获胜後,可以透过某个特定按钮重新浏览胜利画面,
我可以修改程式码後,透过按钮重新预览 (像 java 的 hot-deploy )结果。
这些都相当倚赖於程式码的实作,而你能够越轻松(改越少code甚至不用改)
达到缩短测试路径到能接受的程度,我称之为越有「可测试性」。
-----------------------------------
另外关於可测试性的还有其他的问题就是,
比方说今天这个测试环境需要资料库的协助,
偏偏资料库在远方或者没有测试用资料库,测试时也会有状况。
也就是环境相依性。
-----------------------------------
可测试性可以用很多种方式呈现,我不想用 IOC 这种高级例子说明,
所以我们以第一篇中的停车场的例子说明好了。
main(){
//假设使用者输入一行资料(停车几分钟)并转数字
//并假设使用者都是善良user,不会输入英数字
int k = console.getInt();
int hours = ceil(k/60) ; //除60并且无条件进位
int money = 50 * hours;
print(money);
}
这样的范例,基本上他的可测试性会低於以下的程式码。
-----------------
main(){
//假设使用者输入一行资料(停车几分钟)并转数字
//并假设使用者都是善良user,不会输入英数字
int k = console.getInt();
print(countMoney(k));
}
int countMoney(int minute){ //将计算封装
int hours = ceil( minute/60) ; //除60并且无条件进位
int money = 50 * hours;
return money;
}
-----------------
理由是如果我今天要测试不同的 minute 的测资,
我只需要把 main 修改成这样
main(){
print(countMoney(1));
print(countMoney(61));
print(countMoney(121));
}
相较於原本的版本而言,这样的修改跟出错的机率显然小很多。
原本的作法我们可能会需要写成
int inputs = [1,61,121];
for(int i = 0 ; i < inputs.length ; ++i )
int k = inputs[i];
int hours = ceil(k/60) ; //除60并且无条件进位
int money = 50 * hours;
print(money);
}
或者抽 method ,是不是看起来复杂跟改多了,
而且你可能需要直接动到你测试对象的程式码。
这也是为什麽我们应该尽量将功能函式化降低耦合,
因为可以提供相对简单的测试范围。
模组化带来的优势,也有很大是在这一块。
对於那些异动频繁或者难以验证的程式码,
特别需要针对可测试性作结构上的加强。
而一旦你开始进行针对相对於整体的小单元去进行测试的过程,
基本上你就慢慢走离整合测试的路线,而开始往单元测试的路线前进。
基本上我是这样一步一步往单元测试走,
如果不先了解什麽是可测试性跟慢慢在开发中累积验证程式码的经验,
只想对着一整套的系统找出几个勉强的区块作单元测试根本是缘木求鱼。
当我开始发现测资、测试路径,并且开始观察程式码的可测试性,
我自然会发现那些重要的核心程式码,因为他们太常需要被测试,
而已经慢慢的以某些形式,
被转化成比其他部份的程式码更有可测试性的版本。
我们接下来一篇会继续谈到,我们眼前这样修改程式码的测试,
所累积下来的经验,跟他有什麽缺点。
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 223.143.225.151
※ 编辑: TonyQ 来自: 223.143.225.151 (03/25 21:28)
1F:推 samsonjr:推,浅而易懂却又让新人值得思考的经验知识分享qq 03/25 23:14
※ 编辑: TonyQ 来自: 220.133.44.37 (03/26 14:19)
3F:推 ggg12345:测试的难易度跟是否具可量测性?似乎没有先厘清可以->难易 03/28 01:05
4F:推 ggg12345:有输入无输出根本无从测起,不必输入就有输出是试不出变化 03/28 01:10
5F:→ TonyQ:的确有些东西是无法测试的,至於难易度的量测,我觉得取决於 03/28 01:13
6F:→ TonyQ:经验居多。 03/28 01:13
7F:推 ggg12345:具备输入与输出才能谈试与测,有目的的测试就有涵盖与难易 03/28 01:16
8F:→ TonyQ:当然,这部份跟 yoco 提到的 pure function 是一样的概念 03/28 01:23
9F:→ TonyQ:越能够以清楚的 input 跟 output 表达的就越容易测试 03/28 01:23
10F:→ ggg12345:testability of code是指是否可快速有效又全面性测出正误 03/28 01:24
11F:→ TonyQ:至於难易,目前主要的困难还是在测试对象(输出)的明确跟输 03/28 01:24
12F:→ TonyQ:入的资料准备。 03/28 01:24
13F:→ TonyQ:「全面性」?我不觉得测试 code 有所谓全面性可言,那取决於 03/28 01:25
14F:→ TonyQ:你对测资的掌握程度,而我认为它是个99% 以下的东西,永远有 03/28 01:25
15F:→ TonyQ:1% 是你无法掌握的意外。 03/28 01:25
16F:→ TonyQ:我宁可说,那是个「可靠度」。 03/28 01:26
17F:推 ggg12345:程式的对错(或特性)是可以证明或推定的,当然是假设执行的 03/28 01:35
18F:→ ggg12345:载体是照理想做事的.但载体有其极限(如位元数),因此有适 03/28 01:38
19F:→ ggg12345:用范围.通常测试是为了验证合乎需求?有限的需求就可测试! 03/28 01:44
20F:→ ggg12345:容易或不容易快速测定正误是被追求的,写的想要验的也想要 03/28 01:50
21F:→ ggg12345:实体的机器会有难达理想状况的意外,做为数理逻辑的code是 03/28 01:57
22F:→ ggg12345:依照特定的功能性运作,若不违反可测性就能被全面性论定. 03/28 02:02
23F:推 ggg12345:不过,要做得涵盖面够齐全,又快速测定那就会有难易问题了! 03/28 02:08
24F:→ TonyQ:有限也要时间允许啊,NP 问题也很有一些是有限解但解不完的 03/28 11:11