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

mit6.031 2023spring 软件构造 笔记 Testing

当你编码时,目标是使程序正常工作。 但作为测试设计者,你希望让它失败。 这是一个微妙但重要的区别。

为什么软件测试很难?

  • 做不到十分详尽:测试一个 32 位浮点乘法运算 。有 2^64 个测试用例!
  • 随机或统计测试效果差:其他工程学科可以测试小的随机样本(例如制造的硬盘驱动器的 1%)并推断整个生产批次的缺陷率。但是对于软件来说并非如此。软件行为在可能输入的空间内不连续且离散地变化,系统可能在广泛的输入范围内似乎工作正常,然后在单个边界点突然失效,堆栈溢出、内存不足错误和数字溢出错误往往会突然发生。

Test-first programming

顺序:

  1. Specification (规范):编写函数签名和注释,明确其行为、输入约束和输出。
  2. Test (测试):根据规范编写测试用例。
  3. Implementation (实现):编写实现代码,并通过已写的测试。

测试优先编程的最大好处是防止错误。 不要将测试留到开发结束,因为此时有一大堆未经验证的代码。 将测试留到最后只会使调试时间更长、更痛苦,因为错误可能存在于代码中的任何位置。

Systematic testing

我们希望进行系统测试,而不是详尽、随意或随机的测试。系统测试意味着我们以有原则的方式选择测试用例,目标是设计一个具有三个理想属性的测试套件:

通过分区选择测试用例

我们希望选择一组足够小的测试用例,以便于编写和维护并快速运行,但又足够彻底以发现程序中的错误。

为此,我们将程序的输入空间划分为多个子域,每个子域由一组输入组成。我们只需要为每个集合测试一个代表。 这种方法通过选择不同的测试用例,并强制测试探索随意或随机测试可能无法到达的输入空间区域,从而充分利用了有限的测试资源。

例:

Math.abs()

测试用例:

  • a = 17 覆盖子域 a > 0
  • a = 0 覆盖子域 a = 0
  • a = -3 覆盖子域 a < 0

Math.max()

测试用例:

  • (a,b) = (1, 2) 覆盖 a < b
  • (a,b) = (10, -8) 覆盖 a > b
  • (a,b) = (9, 9) 覆盖 a = b

子域应具有三个理想的属性:

  • 互斥
  • 完整
  • 非空

自动化单元测试

  • 单元测试 (Unit Test):测试单个模块(如函数)的测试。
  • 自动化:使用测试框架(如Mocha for JS/TS)编写测试代码,自动运行并检查结果(使用assert.strictEqual, assert.deepStrictEqual等断言),输出通过/失败报告。
  • 文档化测试策略 (Documenting Strategy):在测试代码中以注释形式记录所采用的分区策略,并为每个测试用例命名其所覆盖的子域(如it("covers a < b", ...)),这极大地增强了测试套件的可理解性。

黑盒 vs 玻璃盒测试

  • 黑盒测试仅根据规范选择测试用例,不查看实现代码。这是测试优先编程的天然方式。
  • 玻璃盒测试基于对实现代码的了解选择测试用例(例如,测试不同的算法分支、内部缓存机制等)。
  • 结合使用:先进行黑盒测试(定义分区),再通过玻璃盒测试和覆盖率分析来补充测试用例,提高彻底性。

覆盖率

衡量测试套件对代码的覆盖程度,常用指标:

  • Statement coverage (语句覆盖):是否每条语句都被至少一个测试执行过?(常见目标)
  • Branch coverage (分支覆盖):是否每个控制分支(如if/else的两边)都被至少一个测试执行过?
  • Path coverage (路径覆盖):是否所有可能的执行路径都被覆盖?使用工具(如Istanbul/nyc)测量覆盖率,并补充测试用例以提高覆盖率

单元测试 vs. 集成测试

  • 单元测试孤立地测试单个模块。优点:错误更容易定位(就在被测试的模块中)。
  • 集成测试:测试多个模块的组合或整个系统。必要但错误可能出现在任何连接的模块中。
  • 策略:首先依靠全面的单元测试建立对各个模块的信心,然后使用集成测试来检查模块间的交互。尽量避免在单元测试中依赖其他可能出错的模块

自动化回归测试

  • 回归测试 (Regression Testing):在每次修改代码后(修复bug、添加功能、优化性能)运行完整的测试套件,防止修改引入新的错误
  • 测试优先调试 发现bug时,立即编写一个能重现该bug的测试用例,并将其加入测试套件。修复bug后,该测试用例就成为防止未来回归的回归测试
  • 自动化是回归测试可行的关键。

迭代式测试优先编程

软件开发不是线性的,应采用迭代方式:

  1. 编写初步规范和测试。
  2. 编写初步实现。
  3. 根据实现中发现的问题,迭代改进规范、测试和实现。
    迭代允许更快地获得反馈,更有效地利用时间,特别是在解决复杂问题时。
http://www.dtcms.com/a/360615.html

相关文章:

  • 自定义创建Linux内核Tracepoint
  • git的子模块讲解
  • mmaction安装的详细说明帖
  • 【ArcGIS微课1000例】0150:如何根据地名获取经纬度坐标
  • 基于springboot的摄影器材租赁回收系统
  • 疯狂星期四文案网第56天运营日记
  • LeetCode 36. 有效的数独 - 解题思路与实现详解
  • arnold图像加密(猫脸变换)
  • AIGC应用与实践 - 实验3:使用豆包生成播客
  • 赵玉平《刘备谋略》读书笔记(上部)
  • zookeeper集群是什么技术, 有什么作用
  • 第三阶梯:变动感知——在流沙之上,建造你的灯塔
  • 在开发过程中经常遇到 OOM(内存溢出)问题,如何解决?
  • __getitem__()方法的神奇
  • 【LeetCode修行之路】算法的时间和空间复杂度分析
  • 2000w 的数据量,mysql要进行几次IO操作,为什么
  • GEE 实战:Landsat 5 月度 NDVI 数据插值填补(以 8 月为例)_后附完整代码
  • sting模拟实现
  • 前后端联合实现多个文件上传
  • FastAPI 教程:构建高性能异步 API 服务
  • 石化设备健康管理平台:工业智能化转型的关键使能技术​
  • std::thread详解
  • Spring Boot单体项目整合Nacos
  • C++17 折叠表达式(Fold Expressions)详解
  • ConcurrentHashMap在扩容的过程中又有新的数据写入是怎么处理的
  • 《Bishop PRML》10.1 (3) 理解VAE reconstruction loss
  • Redis 中的 Bitmap 与 Bitfield 及 Java 操作实践
  • python如何下载svg图片
  • 【Proteus仿真】数码管控制系列仿真——单个数码管控制/多数码管控制
  • leetcode 260 只出现一次的数字III