当前位置: 首页 > news >正文

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

文章目的

显示不同的博客能获得多少博客质量分
(这是关于博客质量分的测试 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

相关文章:

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

文章目的 显示不同的博客能获得多少博客质量分 (这是关于博客质量分的测试 https://www.csdn.net/qc) 这个博客得了 83 分。怎么才能得到更多分数? 正文 我们谈了不少测试的名词, 软件是人写的, 测试计划和测试用例也是人写的, 人总会犯错误。错误发生…...

Linux文件基础I/O

文件IO文件的常识基础IO为什么要学习操作系统的文件操作C语言对于函数接口的使用接口函数介绍如何理解文件文件描述符重定向更新给模拟实现的shell增加重定向功能为什么linux下一切皆文件?文件的常识 1.空文件也要在磁盘占据空间 2.文件 内容 属性 3.文件操作 对…...

HTML看这一篇就够啦,HTML基础大全,可用于快速回顾知识,面试首选

HTML 1 基础 1.1 DOCTYPE <!DOCTYPE> 文档类型声明&#xff0c;作用就是告诉浏览器使用哪种HTML版本来显示网页。 <!DOCTYPE html> 这句代码的意思是: 当前页面采取的是 HTML5 版本来显示网页. 注意: 声明位于文档中的最前面的位置&#xff0c;处于 标签之前。 …...

Altium Designer(AD)软件使用记录05-PCB叠层设计

目录Altium Designer(AD)软件使用记录05-PCB叠层设计一、正片层和负片层的介绍1、正片层(Signal)2、负片层(Plane)3、内电层的分割实现二、正片层和负片层的内缩设计1、负片设置内缩20H原则2、正片铺铜设置内缩1、设置规则2、重新铺铜三、AD的层叠设计四、叠层设计需要注意的问…...

ArcGIS动态表格批量出图

一.产品介绍&#xff1a;ArcGIS动态表格扩展模块Mapping and Charting Solutions&#xff0c;可用于插入动态表格&#xff0c;与数据驱动结合&#xff0c;出图效率无敌。注&#xff1a;优先选择arcgis10.2.2。 二、下载连接&#xff1a; https://www.xsoftnet.com/share/a001CX…...

ChatGPT真神奇,但是也真焦虑

ChatGPT火爆ChatGPT的火爆程度不用说也知道。就目前来说&#xff0c;已经开始冲击各行业了&#xff0c;比如客服、智能助手、语言学习、自然语言处理等等等。。ChatGPT冲击冲击最高的可能就是中间这个段位的了。高段位无法取代&#xff0c;但是低段位&#xff0c;通过使用ChatG…...

mos管驱动与米勒平台介绍、消除

mos驱动设计 1.选择适当的驱动芯片 为了控制MOSFET&#xff0c;需要使用专门的驱动芯片。选择合适的芯片需要考虑MOSFET的电压和电流需求。常见的驱动芯片包括IR2110、IR2184、MIC4424等。 2.设计电路 在驱动电路中&#xff0c;需要加入一些电路元件来保证MOSFET的顺畅工作…...

20230311英语学习

Philosophy of Food: Guidelines for an Authentic Approach to Eating 饮食哲学&#xff1a;值得思考的问题 Whats Philosophical About Food? Philosophy of food finds its basis on the idea that food is a mirror.Eating mirrors the making of a self, that is, the …...

【面试题】Nginx面试题汇总(无解答)

什么是Nginx&#xff1f;谈谈个人都理解&#xff0c;项目中是否用到&#xff0c;为什么要用&#xff0c;有什么优点&#xff1f;为什么要用Nginx&#xff1f;为什么Nginx性能这么高&#xff1f;Nginx怎么处理请求的&#xff1f;什么是正向代理和反向代理&#xff1f;使用“反向…...

Java面试总结(六)

进程和线程的区别 根本区别&#xff1a; 进程时操作系统资源分配的基本单位&#xff0c;而线程是处理器任务调度和执行的基本单位。 资源开销&#xff1a; 每个进程都有自己独立的代码和数据空间&#xff08;程序上下文&#xff09;&#xff0c;进程之间的切换开销比较大&…...

Windows逆向安全(一)C与汇编的关系

前言 逆向是一种新型的思维模式也是软件开发领域中极为重要的技术&#xff0c;涵盖各种维度去深挖软件架构的本质和操作系统原理&#xff0c;学习逆向后可以在各领域中发挥至关重要的作用&#xff0c;其中包括黑灰色&#xff0c;安全开发&#xff0c;客户端安全&#xff0c;物…...

Lazada、Allegro、速卖通测评自养号技术(方法解析)

无论是亚马逊、拼多多Temu、shopee、Lazada、wish、速卖通、煤炉、敦煌、雅虎、eBay、TikTok、Newegg、乐天、美客多、阿里国际、沃尔玛、OZON、Joom、Facebook、Coupang、独立站、Cdiscount、Kaufland、DARTY、Allegro、MANO等平台测评自养号对于卖家来说算是一种低成本、高回…...

Vue3的composition API—setup函数, ref函数,reactive函数

1、Setup 函数 1.setup 是vue3中的一个配置项 2、setup是所有组件所需要的数据和方法都需要配置到setup中的 3、setup两种返回值&#xff1a; 若返回一个对象 若返回一个渲染函数 mian.js文件 注意&#xff1a;尽量不与Vue2混用 setup中无法访问vue2中的配置 不能是async函数…...

国外seo比较好的优化方法有哪些?

随着互联网的不断发展&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;变得越来越重要。 对于国外市场&#xff0c;Google搜索引擎是最为重要的搜索引擎之一&#xff0c; 因此在优化国外网站时&#xff0c;需要将Google SEO优化作为首要任务。 关键词研究和优化 在进行…...

【JavaEE进阶】——第一节.Maven国内源配置

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 操作步骤 1.打开项目配置界面&#xff08;当前项目配置&#xff09; 2.检查并配置国内源 3.再次打开项目配置界面&#xff08;新项目配置&#xff09; 4…...

dockerFile编写

dockerFile编写 语法参数 # DockerFile常用指令 USER # 指定运行的用户&#xff0c;一般不用配置 FROM # 拉取基础镜像&#xff0c;一切从这里开始构建 ARG # 构建参数&#xff0c;只能在dockerFile中使用, # eg: JAR_FILEtarget/springboot-mongo-0.0.1-SNAPSHOT.jar MAI…...

jenkins扩展你的流水线

文章目录一、概述二、可信库和不可信库可信库不可信库三、内部库与外部库内部库SSH访问HTTP 访问外部库配置一个外部库四、在流水线脚本中使用库从源码版本控制中自动下载库加载库到脚本中Library 注解库步骤库指令五、Jenkins 项目中的库范围六、共享库代码的结构src示例一&am…...

Golang模糊测试入门

本教程介绍了 Go 中模糊测试的基础知识。通过模糊测试,随机数据会针对您的测试运行,以试图找到漏洞或导致崩溃的输入。可以通过模糊测试发现的一些漏洞示例包括 SQL 注入、缓冲区溢出、拒绝服务和跨站点脚本攻击。 在本教程中,您将为一个简单的函数编写模糊测试,运行 go 命…...

ARM uboot 的移植4 -从 uboot 官方标准uboot开始移植

一、添加DDR初始化1 1、分析下一步的移植路线 (1) cpu_init_crit 函数成功初始化串口、时钟后&#xff0c;转入 _main 函数&#xff0c;函数在 arch/arm/lib/crt0.S 文件中。 (2) 在 crt0.S 中首先设置栈&#xff0c;将 sp 指向 DDR 中的栈地址&#xff1b; #if defined(CONF…...

不用索引怎么优化百亿数据? | MySQL性能优化篇

文章目录数据库调优一、数据库调优原理1.1 为什么要进行MySQL数据库调优&#xff1f;1.2 什么影响数据库性能&#xff1f;1.3 数据库调优到底调什么&#xff1f;二、数据库压力测试2.1 什么是压测&#xff1f;2.2 JMeter简介2.3 驱动下载2.4 测试过程三、连接池3.1 压力测试连接…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...

Java并发编程实战 Day 11:并发设计模式

【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天&#xff0c;今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案&#xff0c;它们不仅提供了优雅的设计思路&#xff0c;还能显著提升系统的性能…...

深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学

一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件&#xff0c;其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时&#xff0c;价带电子受激发跃迁至导带&#xff0c;形成电子-空穴对&#xff0c;导致材料电导率显著提升。…...