现代软件工程讲义 9 测试 关于闰年的测试

我们谈了不少测试的名词, 规范和原则 (link1, link2). 软件是人写的, 测试计划和测试用例也是人写的, 人总会犯错误。错误发生之后, 总有人问: 为什么这个bug 没有测出来啊?!   我们看看一类简单的bug是如何发生的,以及如何预防它们再度发生: 

闰年

软件少不了和日期打交道, 日历系统算是人类的一个 legacy system, 这个系统在逐步进化的过程中, 打了好多补丁, 闰年就是补丁之一, 现在的spec 是: 4 年一闰, 100 年不闰,400年又闰。

 

错误之一: 算不清那一年是不是闰年。 1900 年是闰年么?

电子表格软件Excel 就有这样一个BugExcel 的日期计算功能认为1900年是一个闰年,这是不对的,但是它愣是一直没有改正这个错误。为什么屡教不改呢?

事是这样的,1980 年代, 这类电子表格软件的市场领头羊是Lotus 1-2-3一款软件。

 

image

来源: http://en.wikipedia.org/wiki/Lotus_1-2-3 

 

Lotus 1-2-3 占据了大部分市场份额, 不过, 它的日期计算功能有一个小Bug,就是把1900 年当作闰年。这类软件在内部把日期保存为“从1900/1/1 到当前日期的天数”这样的一个整数。Excel 作为后来者,要支持 Lotus 1-2-3 的数据文件格式,这样才能正确处理别的软件产生的格式文件。  这个错误就这么延续下来了,每一版本都有人报告,但是都没有改正。我们可以在Excel 中试试看:

在任意格子(cell)中输入“=DATE(1900,2,28)”,并且定义这个格子的格式为数字。大家可以看到数值变为:59表明1900/2/28 1900/1/1开始的第59天。

输入“=DATE(1900,2,29)”,可以看到 60! 这是一个不存在的日期!

输入“=DATE(1900,3,1)”,数值是61,事实上,这应该是60。从这一天开始的所有日期都错了一天。

 

改这个问题,技术上一点问题都没有。但是在现实中会出现下列问题:

1)几乎所有现存文件的日期数据都要减少一天,所有依赖于日期的 Excel公式也要做检查和修改。可以想象在计算利率,判断日期是否相等这些问题上都会出现细小而不能忽视的问题。 这在现实生活会造成很大的麻烦。

2Excel的日期问题解决了,但是其他软件还是有这个Bug,数据文件在不同软件中使用,就会有很头痛的兼容性问题。

 

下面是C# 的代码片段, 这段程序对么?

public static bool IsLeapYear(int year)
{
    System.Diagnostics.Debug.Assert(year >= 1900);
    if (year % 400 == 0)
        return true;
    if (year % 100 == 0)
        return false;
    if (year % 4 == 0)
        return true;
    return false;
}

 

错误之二: 计算错误

一个应用程序从另一个模块中接到一个数值, 是当天距离 [1980/1/1] 的天数, 现在要求这个程序返回今天的年份。 下面的程序怎么样? 有bug 么?

public static int NumberToYear(int days)
{
    int year = 1980; /* start with 1980 */
    System.Diagnostics.Debug.Assert(days >= 0);

    while (days > 365)
    {
        if (IsLeapYear(year))
        {
            if (days > 366)
            {
                days -= 366;
                year ++;
            }
        }
        else
        {
            days -= 365;
            year ++;
        }
    }
    return year;
}   

 

如果你要写这个程序的单元测试, 你会列出多少个测试用例? 你们保证所有代码路径都被覆盖么?

 

要写测试用例, 一个暴力的做法是穷举所有例子, 但是这有问题:

    1. 你穷举不完
    2. 即使穷举了很多例子, 但是它们未必能帮助发现 独特 的问题.  例如你可以测试输入 为 100, 101, 102, 103, 104, … 如果这个程序能正确处理 100, 它似乎也能处理101… 这些数。

我们要引入 “等价类 (Equivalence)” 这一概念。 一个粗浅的做法是:

如果一个函数可以返回 true | false, 你至少得有两类测试集合, 让它分别返回 true | false

如果你知道这个函数工作的原理, 或者了解程序要反映的现实世界, 你可以举出更详细的等价类, 例如针对 IsLeapYear():

被 400 整除的年份

被 100 整除, 但是不被400 整除的年份

被 100 整除, 同时被400 整除的年份

被 4 整除, 但是不被100 整除的年份

被 4 整除, 同时被100 整除的年份

偶数, 不被4 整除的年份

奇数年份

其它非法输入的年份

程序员都知道程序经常在边界条件附近出错, 针对IsLeapYear(), 你可以得出下面两个测试用例:

设计允许的最小的年份

设计允许的最大的年份

啊, 设计中没有考虑这个?   那这个设计要出现问题。  在1950-70 年代, 很多程序用两位数字表示年份 (00 – 99), 那些聪明的程序员认为这已经足够了, 没想到这些程序和设计影响了很多要和它们兼容的程序 (就像 Excel 要兼容 Lotus 1-2-3 那样), 到了1990年代后期, IT 业花了很多人力物力来解决 Y2K 的千年虫问题。 一些程序员非常钟爱的 UNIX 操作系统 (32 位) 也有自己的千年虫问题, 它会发生在 2038 年! 到时候人们还会用32位的机器么? 也许在一个大家想不到的关键部位, 一些老旧的, 嵌入式的 Unix 系统会悄悄地发作…

 

除了从外部的输入/输出来设计测试用例, 我们也可以从内部考虑, 看看这些测试用例是否把所有语句都覆盖了。 但是要注意, 即使所有语句都被测试用例覆盖了, 程序还是可能出错!

 

例如, 我们测试 NumberToYear(), 分析它的各个条件, 我们推算出我们的数据要覆盖下面一些情况:

输入的 day 大于 365

输入的 day 小于 365

输入的 day 大于 366  并且1980 年到那一年中, 至少有一年是闰年, 例如输入一个2008年的某一天。

输入的 day 大于 366  并且1980 年到那一年中不包括闰年。

这样是不是就把所有路径都包括了? 程序就没有错了?

不巧的是, 这个程序用在了某著名公司的产品上, 出品的前两年没什么事, 到了2008 年的最后一天 (那一年有366 天), 出了一个问题:

 

年份一直增加到了 2008, 这时候, day == 366, 我们看看循环能做下去么?

if (IsLeapYear(year))
{
    if (days > 366)   //day == 366, 不满足条件 
    {
        days -= 366;
        year ++;
    }

所以 day 没有减少, year 也没有增加, 循环又继续下去, 任何条件都没有改变, 进入了死循环!

 

不幸的是, 这个程序经过了种种测试, 进入了市场.  于是, 在2008 年的最后一天, 许多用户发现他们的 Zune Player (只限于 Zune 30 型号) 开机之后就进入死锁状态…

http://www.usatoday.com/tech/products/2008-12-31-zune-mystery_N.htm

http://www.zuneboards.com/forums/showthread.php?t=38143

官方的说法是 - 大家等到明天就好了!  Smile   不用说这对于用户, 对于产品的口碑, 对于这个代码的开发者, 测试者是一个极大的打击!

 

如果你是一个测试人员, 你应该增加什么测试用例呢?  如果用边界条件分析, 应该有至少 4 个新的测试用例:

闰年的第一天;

闰年的最后一天;

平年的第一天;

平年的最后一天

对于程序员, TA 应该如何修改代码?

 

错误之三: 没想到还有闰年

在IT 行业混了很多年的好处之一就是你可以看到不少 bug. 下面又来了一个:

Windows Home Server与客户端connector第一次连接时,需要Server为connector颁发安全证书。出于某种实现上无法避免的原因,客户端的证书日期一定要早于Windows Home Server,否则生成证书的函数会fail。Windows Home Server是2007年7月RTM的。为了方便起见,设计中规定,给客户端生成证书的函数使用2006年作为年份

<完整的故事 link: http://yishan.cc/blogs/lilei105/archive/2008/03/03/885.aspx>

 

作为一个程序员, 你如何实现这个设计呢? 一拍脑袋, 就取当天的日期, 然后把日期中的年字段改成 2006, 不就行了么?

然后到了 2008/2/29 这一天… 程序把日期改成了 2006/2/29. 然后就悲剧了.   

软件团队在自问: 为啥我们当初没测出来?  如果你是测试人员, 你会想到这个测试用例么?

 

错误之四: 闰年bug 一天损失 30 万

上面的错误都是外国软件公司搞的, 我们看看中国的软件 (还是嵌入式的软件) 也不甘落后, 也创出了自己的闰年bug: 

http://news.sohu.com/20120301/n336384709.shtml  

 

-------------------------------

参考阅读:

测试用例的等价类划分和边界条件分析:

http://en.wikipedia.org/wiki/Equivalence_partitioning

http://en.wikipedia.org/wiki/Boundary_value_analysis

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/500423.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

现代软件工程 来自卓越大学教师的建议 (读书笔记)

教师教学有培训和参考书么? 我从来没想到过我会在大学里教书, 而且还教了好几年, 四个学校。 当时接到任务的时候, 我把它当作实习生培训和新员工培训的”学院版”, 还是继续强调实践, 反馈, 合作, 就这么开讲了。 在微软公司, 做大部分和人相关的事情, 都得先有一个培训, …

软件工程讲义 9 创新的出路 走进作坊

我第一次注意到 “作坊”这个词和软件行业联系起来大概是这个 2004 年 11 月的报道: 标题: 信产部副部长娄勤俭&#xff1a;中国软件业还在手工作坊阶段 日前&#xff0c;信息产业部副部长娄勤俭在出席中国软件产业生态链高层论坛时表示&#xff0c;中国软件产业的规模还比较小…

现代软件工程 习而学的软件工程教育

茅于轼先生写了一篇博客 ( http://blog.sina.com.cn/s/blog_49a3971d0102dufj.html ) 纪念茅以升先生提出的 习而学的工程教育: 把颠倒了的工程教育顺序恢复过来&#xff0c;即他称之谓“习而学的工程教育”。 以桥梁建筑专业为例&#xff0c;大学一年级先学施工条例&#xff0…

现代软件工程 教课心得

现实世界是最好的老师, 我们这些叫 “老师” 的人, 充其量是个助教。 但是有些助教却不让学生见到老师。 **************** 老师都想把课教好, 学生都想把课学好. 但是我们常常看到一个学期过后, 老师, 学生都有很多抱怨 (例如: 各种良好愿望和计划在实施中的问题). 看了上…

微软学术搜索项目 10个版本的历程

这是我在微软亚洲研究院参与的项目之一, 从 2009 年秋天开始, 我们小组把它从一个研究原型发展为涵盖全学科的学术搜索门户。 它索引了 4千万论文, 2千万作者, 6 大实体类型, 8 种数据可视化功能, 具有开放的API 平台和手机客户端. 下面说说项目的发展: 2009/8: 内部发布 alp…

现代软件工程讲义 9 测试 QA 的角色和分工

测试的角色 (Test) 要独立出来么 ? 独立出来的测试角色怎么才能发挥作用? 有些成功人士和成功的公司号称没必要有独立的测试角色 (Test), 你怎么看? 最近又看到一些关于开发人员要不要负责测试的讨论。 例如: http://www.aqee.net/on-testers-and-testing/ 大多数的开发…

程序设计作业: 车模+数模 = ?

我上学的时候只听说过 “航模”, 没听说过“数学建模”这门学问. 这几年在简历里看到过不少人号称数模得过什么奖之类的, 我都没好意思问太仔细。 在帝都开车经常遇到堵车, 我于是想到了一个车模的问题。 我想请大家帮着给这个车模搞个数模, 求个解法: 想象帝都北四环或北五…

计算机考研的调查和改进建议

几星期前, 我在微博上讨论考研的事, 有专家建议不如把意见整理出来, 说不定可以转告给相关方面。 我没有考过研, 问了公司的同事们, 绝大多数都是保研的, 也没考过。 我从网上下了一份模拟题, 好像还挺难&#xff0c;有一种要翻书的冲动。 全国有多少学生为了考研而奋斗? …

2012 夏季高校微软俱乐部活动 - 开门创新

创新啊创新, 大家都在讲创新。 一般的理解, 创新就是公司内部关起门来想, 实验, 内部评审, 然后申请专利什么的, 其实也有开门创新的办法: http://www.innovationexcellence.com/blog/2012/08/13/40-examples-of-open-innovation-crowdsourcing/ it is about bringing extern…

笔记 - 高等教育的创新

教育是一个社会发展的支柱, 你和我能看到并理解这个博客, 教育功不可没。 高等教育的形式并不是一成不变的, 高等教育一直在演进, 变革中, 最近一股“online higher education” 的浪潮在美国兴起, 貌似突兀, 其实有规律可循。 在关注最近的在线教育浪潮之前, 我们看看美国高等…

现代软件工程讲义4 Scrum/Sprint

Advanced Software Engineering, Development Process, Scrum/Sprint 软件开发的流程有很多 (看 各种方法论概述), 我也写过一篇博客 (酒后的敏捷) 谈了谈最近比较时髦的开发流程。 今天我们不喝酒, 正襟危坐地说说敏捷这一路 Scrum/Sprint 开发方法. 从理论上看, 这个方法真…

现代软件工程讲义 7 设计阶段 Spec

在前一个博客里 (典型用户), 我们讲了怎么收集, 分析和验证用户的需求。 这里我们讲 spec – specification Specification, 又叫spec, 有两种: a) functional spec, 软件功能说明书, 主要用来说明软件的外部功能, 和用户的交互情况 (把软件当作一个黑盒子) b) technical spec…

现代软件工程 2012 北航 项目复审模板

这是现代软件工程课在北航的项目复审要求。 这次我们有下列 10 个团队, 他们做了一些有意思的项目&#xff1a; 有七个小组合作&#xff0c;携手打造一个叫 学霸 的网站: 100Years 网页收集和归类工具76er 网页收集和归类工具FightingSnail 网页元数据抽…

现代软件工程讲义 8 软件的血型

[这是 现代软件工程讲义 的一篇] 一个软件团队经历了计划/设计/开发等阶段, 达成代码完成 (Code Complete) 这一目标&#xff0c;似乎后面的事情就水到渠成了. 其实不然, 软件生命周期的最后阶段往往是最考验团队的&#xff0c;不但考验团队项目管理水平&#xff0c;应变能力…

现代软件工程讲义 6 用户调研

[现代软件工程讲义 的一部分] 软件开发的过程, 就是 “用户最需要的东西” 在下面这一链条中传送&#xff0c;转换&#xff0c;实现&#xff0c;扭曲或丢失的过程。 用户最需要的 > 用户表达出来的 > 软件团队能理解的 (老板/PM) 团队的商业目标 > 软件团队成员具…

软件工程讲义 0 微博上的软件工程

[现代软件工程讲义] 有舌尖上的美味, 也有微博上的软工。舌尖上的美味各有千秋, 而微博上对软工的抱怨都是相似的。 下面是我在新浪微博收集到大学生对软件工程教学的反馈: 师生关系&#xff08;不限于软件工程&#xff09; 教材 上课 & 老师 实践 & 作业 考试 考完…

现代程序设计 作业 2

我们上节课讲了 返回整数数组中最大子数组的和 这个问题。 我们第二次作业在这个基础上扩展。 程序要使用的数组放在一个叫 input.txt 的文件中, 文件格式是: 数组的行数, 数组的列数, 每一行的元素, (用逗号分开) 每一个数字都是有符号32位整数, 见 MSDN 的定义. 当然, 行…

现代程序设计 作业 3

这个作业是采取结对编程的方式完成。 在上一个作业中, 我们尝试了各种命令行的处理&#xff0c;以及各种数组的处理。 现在&#xff0c; 我们要把 现代程序设计 作业 2 的各个结果转换成图形界面显示。这个问题看起来很难, 实际上大部分难的工作都在上一个作业完成了 (数组计…

现代程序设计 作业4

英语国家的小孩们经常玩 Word Search 的游戏, 就是在一个填满字母的矩阵中把单词找出来。 这是一个简单的例子: &#xff08;来自 wikipedia&#xff09; 这是一个比较复杂的例子: 这是答案: 美国的商店里还有不少 word search books 卖, 两三块钱一本。 让我们把这个有趣的…

现代程序设计 作业6 - 简单而有意义的题目

这是这个课件的一部分: 现代程序设计 &#xff08;课程设计中, 征求意见稿&#xff09; 好多同学们都说题目难&#xff0c;这回我们来一个简单而很有意义的。 :&#xff09; 写代码爽还是读代码爽? 往一堆乱麻中再加上一些线索&#xff0c;似乎比较容易&#xff1b;然而从…