软件测试的案例分析 - 闰年5

news2024/12/26 11:02:56

文章目的

显示不同的博客能获得多少博客质量分
(这是关于博客质量分的测试 https://www.csdn.net/qc) 这个博客得了 83 分。怎么才能得到更多分数?

正文

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

闰年

软件少不了和日期打交道, 日历系统算是人类的一个遗留系统 (legacy system), 这个系统在逐步进化的过程中, 打了好多补丁, 闰年就是补丁之一。

关于闰年,现在的 规格说明书 (spec) 非常简单:

年份被 4 整除的,就是闰年, 但是被 100 整除的年份不闰,被 400 整除的年份又是闰年。

但是,人们在写软件的时候,还是犯了不少错误。

错误之一

下面是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;
}

1900 年是闰年么? 根据我们上面说的规格说明书,这不是一个闰年。
如果你要写这个程序的单元测试, 你会列出多少个测试用例? 你们保证所有代码路径都被覆盖么?

要写测试用例, 一个暴力的做法是穷举所有例子, 但是这有问题 – 你穷举不完
即使穷举了很多例子, 但是它们未必能帮助发现 独特 的问题. 例如你可以测试输入 为 100, 101, 102, 103, 104, … 但是你仍然不知道新加的一个测试数字 105 是否测试了 以前没有测试过的情况, 也就是说, 105 这个测试数据,可能和 100,101 这些数字都是等价的。 但是独特的测试数据,可能还有很多。

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

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

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

  • 被 400 整除的年份
  • 被 100 整除, 但是不被400 整除的年份
  • 被 100 整除, 同时被400 整除的年份
  • 被 4 整除, 但是不被100 整除的年份
  • 被 4 整除, 同时被100 整除的年份
  • 偶数, 不被4 整除的年份
  • 奇数年份
  • 其它非法输入的年份

但是,在全世界被广泛使用的电子表格软件 Excel 就有这样一个Bug:Excel 的日期计算功能认为1900年是一个闰年。
故事是这样的,在 PC 萌芽的 1980 年代, 这类电子表格软件的市场领头羊是Lotus 1-2-3这一款软件:
在这里插入图片描述
来源: http://en.wikipedia.org/wiki/Lotus_1-2-3
Lotus 1-2-3 占据了大部分市场份额,这类软件在内部把日期保存为 “从1900/1/1 到当前日期的天数” 这样的一个整数。 不过,它的日期计算功能有一个小Bug,就是把1900 年当作闰年。这样,内部保存的 “天数” 就是多算了 1900 年并不存在的 2/29 号。 Excel 作为后来者,要支持 Lotus 1-2-3 的数据文件格式,这样才能正确处理别的软件产生的格式文件,Excel 只好也这么照做。 这个错误就这么延续下来了,每一版本都有人报告,但是都没有改正。我们可以在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。从这一天开始的所有日期都错了一天。

改正这个bug,技术上一点问题都没有。

改好了,我们更新 Excel 的版本,发布吧! 但是在现实中会出现下列问题:

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

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

错误之二: 计算错误

一个应用程序从另一个模块中接到一个数值, 是当天距离 [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;
}   

程序员都知道程序经常在边界条件附近出错, 针对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 型号) 开机之后就进入死锁状态…

Microsoft says Zune players working again - USATODAY.com

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

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

正确而简明的算法

也有程序员提出:

@bnu_chenshuo: 文中的函数可用一句话搞定

int NumberToYear(int days)
{       
    return 1980 + 100 * days / 36525;
}

我们看到,这段程序用了 36525 这个魔术数字,这个数字是怎么来的?因为近代科学测量的结果是:地球绕太阳转一圈,准确值是365天小5时48分46秒。如果用天为单位,就是 365.242199 天。

大家觉得这个函数有没有什么 bug? 在今后的 100 年都可以使用么?这个函数有多少条件分支,我们要如何去做分支测试,如何考察整个函数的覆盖率?

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

  • 闰年的第一天
  • 闰年的最后一天
  • 平年的第一天
  • 平年的最后一天

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

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

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

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

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

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

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

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

广州出租车计价器无法识别闰年 损失约30万(图)-搜狐新闻


参考阅读

测试用例的等价类划分和边界条件分析:
http://en.wikipedia.org/wiki/Equivalence_partitioning

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

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

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

相关文章

Shiro学习认证和授权

Shiro学习笔记 认证 思路 获取当前的Subject,调用SecurityUtils.getSubject()方法;判断当前用户是否被认证,即是否已经登陆,调用Subject的isAuthenticated()方法进行判断;若没有认证,则把用户名和密码封…

C++11线程、互斥量以及条件变量

文章目录前言1、创建第一个线程2、线程对象的生命周期、等待和分离3、线程创建的多种方式4、互斥量4.1 独占的互斥量std::mutex4.2 递归独占互斥量recursive_mutex4.3 带超时的互斥量std::timed_mutex和std::recursive_timed_mutex4.4 std::lock_guard和std::unique_lock5、cal…

CSS常用内容总结(扫盲)

文章目录前言相关概念【了解】脚本语言什么是脚本语言脚本语言有什么特点常见的脚本语言什么是动态语言,什么是静态语言动态语言和静态语言两者之间有何区别CSSCSS是什么CSS的特点一、CSS代码怎么写基本语法规则引入方式内部样式内联样式表外部样式代码风格二、CSS的…

JavaWeb——进程详解

目录 一、操作系统 1、定义: 2、操作系统的基本功能: 二、进程 1、定义: 三、进程管理 1、PCB定义 (1)、身份标识 (2)、内存指针 (3)、文件描述符 2、操作系统…

Hadoop入门常见面试题与集群时间同步操作

目录 一,常用端口号 Hadoop3.x : Hadoop2.x: 二,常用配置文件: Hadoop3.x: Hadoop2.x: 集群时间同步: 时间服务器配置(必须root用户): (1&#xff09…

1639_perror的函数功能以及简单测试

全部学习汇总: GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 继续分析之前的shell程序代码,看到了一个fork1的实现。 Fork之前还是看过的,但是也已经忘得差不多了,这个fork1就是fork的一种应…

4.Spring Cloud (Hoxton.SR8) 学习笔记—Nacos微服务治理

本文目录如下:一、Nacos微服务治理Nacos 下载 与 启动Spring Cloud 集成 NacosIDEA 同一个 Application 启动多次一、Nacos微服务治理 Nacos 下载 与 启动 https://github.com/alibaba/nacos/releases Nacos 下载与启动: F:\ProgramFiles\nacos\bin> .\startup…

MyBatis操作数据库

目录 MyBatis 功能架构 学习MyBatis 第一个MyBatis查询 1、创建数据库和表 2、搭建MyBatis开发环境 2.1、在项目中添加MyBatis框架 2.2、配置数据库连接信息 2.3、配置MyBatis中xml的保存路径(规则) 3、添加业务代码 3.1、创建实体类 3.2、构…

stack,queue

stack,queuestack的介绍和使用介绍使用模拟实现queue的介绍和使用介绍使用模拟实现priority_queue的介绍和使用介绍使用模拟实现容器适配器概念标准库中stack,queue的底层结构介绍deque原理缺陷deque作为stack,queue底层默认容器stack的介绍和使用 介绍 stack是适…

哪个牌子的蓝牙耳机音质好?音质比较好的蓝牙耳机排名

蓝牙耳机经过多年发展,无论是在外观设计还是性能配置上都有很大的进步,越来越多的蓝牙耳机开始注重音质表现,逐渐有HIFI音质、无损音质出现在大众视野。那么哪个牌子的蓝牙耳机音质好?接下来,我来给大家分享几款音质比…

【H2实践】之 SpringBoot 与 H2 数据交互

一、目标 本文是【H2实践】之认识 H2,【H2实践】之 SpringBoot 整合的后续。前文分别介绍了 H2 及其简单使用,并完成了 H2 与 SpringBoot 的整合。本文将紧接 【H2实践】之 SpringBoot 整合 探索实用 SpringBoot 结合 JPA 通过 web 接口操作 H2 数据库的…

Python数据分析案例22——财经新闻可信度分析(线性回归,主成分回归,随机森林回归)

本次案例还是适合人文社科领域,金融或者新闻专业。本科生做线性回归和主成分回归就够了,研究生还可以加随机森林回归,其方法足够人文社科领域的硕士毕业论文了。 案例背景 有八个自变量,[微博平台可信度,专业性,可信赖性,转发量,…

Java对象结构与内置锁

Java对象结构与内置锁 Java内置锁的很多重要信息都存放在对象结构中 1.Java对象结构 Java对象包括三部分:对象头、实例数据和对齐字节 对象头: 第一个字段叫做Mark Word,用于存储自身运行时数据,例如GC标志位,哈希…

P1120 小木棍(搜索+剪枝)

题目链接:P1120 小木棍 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 样例输入: 9 5 2 1 5 2 1 5 2 1 样例输出: 6 分析:这道题一看数据范围就知道是搜索,但关键是需要剪枝。 首先我们求出所有木棍的长度和&am…

什么是刺猬理念

一、什么是刺猬理念刺猬理念是指把复杂的世界简化成单个有组织性的观点,一条基本原则或一个基本理念,发挥统帅和指导作用。核心是把事情简单化,把所有的挑战和进退维谷的局面压缩为简单的。二、刺猬理念的寓言故事狐狸是一种狡猾的动物&#…

Maix Bit(K210)保姆级入门上手教程---自训练模型之云端训练

Maix Bit(K210)保姆级入门上手教程系列 Maix Bit(K210)保姆级入门上手教程—环境搭建 Maix Bit(K210)保姆级入门上手教程—外设基本使用 这是K210快速上手系列文章,主要内容是,介绍…

《机器学习》基础概念之【P问题】与【NP问题】

《机器学习》基础概念之【P问题】与【NP问题】 这里写目录标题《机器学习》基础概念之【P问题】与【NP问题】一、多项式&时间复杂度1.1. 多项式1.2.时间复杂度二、P问题 & NP问题2.1. P问题2.2.NP问题2.3.举例理解NP问题-TSP旅行商推销问题三、NP-hard问题&NP-C问题…

PMP项目管理项目成本管理

目录1 项目成本管理概述2 规划成本管理3 估算成本4 制定预算5 控制成本1 项目成本管理概述 项目成本管理包括为使项目在批准的预算内完成而对成本进行规划、估算、预测、融资、筹资、管理和控制的各个过程,从而确保项目在批准的预算内完工。核心概念 项目成本管理旨…

vuex getters的作用和使用(求平均年龄),以及辅助函数mapGetters

getters作用:派生状态数据mapGetters作用:映射getters中的数据使用:方法名自定义,系统自动注入参数:state,每一个方法中必须有return,其return的结果被该方法名所接收。在state中声明数据listst…

PyTorch深度学习实战 | 计算机视觉

深度学习领域技术的飞速发展,给人们的生活带来了很大改变。例如,智能语音助手能够与人类无障碍地沟通,甚至在视频通话时可以提供实时翻译;将手机摄像头聚焦在某个物体上,该物体的相关信息就会被迅速地反馈给使用者&…