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

第27章 测试驱动开发模式:深入测试技术

写在前面


这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许优质的单元测试是一个切入点。 就我个人而言,这本书确实很有帮助。第一次读的时候,很多细节我都不太懂,但将书中内容应用到工作中后,我受益匪浅。比如面对一些让人抓狂的代码设计时,书里的方法能让我逐步深入理解代码的逻辑与设计。 作为一名测试开发工程师,我想把学习这本书的经验分享给大家,希望能给大家带来帮助。因为现在工作中大多使用 Python 代码,所以我把书中JAVA案例都用 Python 代码进行了改写 。

在软件开发领域,测试驱动开发(TDD)模式凭借其独特的优势,成为保障代码质量的重要手段。继前文对TDD基础模式的探讨后,本篇将深入介绍测试模式中更具体的测试技术,涵盖子测试、模拟对象、自分流等多个方面,并配以完整示例代码,帮助开发者更好地理解和应用这些技术。

子测试(Child Test)

概念与应用场景

子测试是一种将大规模测试拆解为小规模测试的技术。当编写的测试规模过大难以调试和运行时,通过编写代表该测试某部分的较小规模测试,先让这些小规模测试运行,之后再引入更大规模的测试。这在遵循“测试 - 重构 - 通过”的TDD三部曲时尤为重要,有助于在测试过程中及时发现和解决问题,避免因测试规模过大而导致的复杂性和不确定性。

示例代码

假设我们要测试一个复杂的电商订单处理系统,该系统涉及订单创建、商品库存检查、价格计算等多个功能。我们可以先对子功能进行子测试,以下是一个简化的订单价格计算子测试示例:

# 定义订单类
class Order:def __init__(self, items):self.items = itemsdef calculate_total_price(self):total = 0for item in self.items:total += item['price'] * item['quantity']return total# 子测试用例
def test_order_price_calculation():order_items = [{'name': 'Item1', 'price': 10, 'quantity': 2},{'name': 'Item2', 'price': 15, 'quantity': 1}]order = Order(order_items)result = order.calculate_total_price()assert result == 35, "Order price calculation is incorrect"

在上述代码中,test_order_price_calculation是一个子测试,它专注于订单价格计算功能的测试,通过模拟订单商品数据,验证价格计算的正确性。

模拟对象(Mock Object)

概念与作用

模拟对象是为测试依赖于价值昂贵或构成复杂资源的对象而创建的伪版本。例如在测试涉及数据库操作的代码时,由于数据库建立耗时、数据维护复杂且可能存在远程存储等问题,通常使用模拟对象替代真实数据库。模拟对象不仅能提升测试的性能和可靠性,还增强了测试的可读性,使测试结果更易于理解和分析。

示例代码

以测试一个用户数据查询功能为例,假设该功能原本依赖于真实数据库查询,现在使用模拟对象进行测试:

# 模拟数据库类
class MockDatabase:def __init__(self):self.data = {1: {'username': 'user1', 'email': 'user1@example.com'},2: {'username': 'user2', 'email': 'user2@example.com'}}def query(self, user_id):return self.data.get(user_id)# 被测试的用户查询类
class UserQuery:def __init__(self, db):self.db = dbdef get_user(self, user_id):return self.db.query(user_id)# 测试用例
def test_user_query():mock_db = MockDatabase()user_query = UserQuery(mock_db)result = user_query.get_user(1)assert result['username'] == 'user1', "User query result is incorrect"

在这个示例中,MockDatabase模拟了真实数据库的查询功能,UserQuery类依赖于该模拟数据库进行用户数据查询,test_user_query测试用例验证了查询功能的正确性。

自分流(Self Shunt)

概念与原理

自分流用于测试一个对象与另一个对象是否正常交互。通过让测试对象与测试用例自身进行交互,而非与期望的对象交互,来判断交互的正确性。这一技术可以动态地更新测试用户界面的状态,例如在测试运行、失败或测试集启动/结束时,能够感知并作出相应反应。

示例代码

以下是一个简单的自分流测试示例,用于测试一个事件监听功能:

# 结果监听器类
class ResultListener:def __init__(self):self.count = 0def start_test(self):self.count += 1# 测试用例
def test_notification():listener = ResultListener()result = "Test Result"# 模拟测试运行时通知监听器listener.start_test()assert listener.count == 1, "Notification count is incorrect"

在这个示例中,ResultListener类作为事件监听器,test_notification测试用例模拟了测试运行时对监听器的通知,并验证了监听器的计数是否正确。

日志字符串(Log String)

概念与用途

日志字符串用于测试被调用的消息队列是否正确。通过设置一个存储日志的字符串,在消息被调用时将记录追加在字符串后面,从而可以清晰地了解测试过程中的消息调用顺序和内容,有助于排查和解决测试中出现的问题。

示例代码

假设我们有一个测试模板方法,其中包含设置、测试和清理方法,并使用日志字符串记录操作过程:

# 被测试类
class WasRun:def __init__(self, name):self.name = nameself.log = ""def setup(self):self.log += "setUp "def test_method(self):self.log += "testMethod "def tear_down(self):self.log += "tearDown "# 测试用例
def test_template_method():test = WasRun("testMethod")result = Nonetest.setup()test.test_method()test.tear_down()assert test.log == "setUp testMethod tearDown ", "Log string is incorrect"

在上述代码中,WasRun类的方法在执行时会将操作记录到log字符串中,test_template_method测试用例验证了日志字符串的内容是否符合预期。

崩溃测试模拟(Crash Test Dummy)

概念与目的

崩溃测试模拟用于测试不太可能被触发的错误码。通过创建一个只抛出异常而不做任何实质工作的对象,使用特定方法触发错误码,以验证系统在面对异常情况时的处理能力,确保未被测试的代码在异常情况下也能正常工作。

示例代码

以测试文件系统满时的异常处理为例:

# 模拟满文件系统的文件类
class FullFile:def __init__(self, path):self.path = pathdef create_new_file(self):raise Exception("File system is full")# 测试用例
def test_file_system_error():file = FullFile("test_path")try:file.create_new_file()assert False, "Expected exception not raised"except Exception as e:assert str(e) == "File system is full", "Exception message is incorrect"

在这个示例中,FullFile类模拟了文件系统满时创建新文件的异常情况,test_file_system_error测试用例验证了系统是否能正确捕获和处理该异常。

中断的测试(Broken Test)

概念与技巧

当独自编写程序时,可能会遇到中途离开的情况。中断的测试技巧是让最后一个测试保持不完整状态,作为下次继续编程的切入点。通过这种方式,能够在重新开始编程时快速回忆起当时的思路,避免因长时间中断而导致的编程上下文丢失。

示例说明

假设我们正在编写一个计算斐波那契数列的程序,中途中断时可以保留一个未完成的测试:

# 斐波那契数列计算函数(未完成)
def fibonacci(n):if n == 0:return 0elif n == 1:return 1# 此处省略后续计算逻辑# return fibonacci(n - 1) + fibonacci(n - 2)# 未完成的测试用例
def test_fibonacci():result = fibonacci(5)# 暂时不进行断言,保留测试的中断状态# assert result == 5, "Fibonacci calculation is incorrect"

在上述示例中,test_fibonacci测试用例在计算斐波那契数列的函数未完全实现时保留中断状态,方便开发者后续继续编写和测试。

干净签入(Clean Check - in)

概念与团队协作

在团队编程中,干净签入要求在签入代码时确保所有测试都能运行通过。这是一种良好的实践,有助于保持代码库的稳定性和可维护性。当团队成员共同开发时,未通过的测试可能会给其他成员带来困扰,因此每次签入前都应进行全面的测试,确保代码的质量和可靠性。

示例场景

假设团队正在开发一个项目管理系统,当某个成员完成了一个新功能的开发,准备签入代码时,需要先运行所有测试用例:

# 假设这里有多个测试用例类和方法
class TestProjectManagementSystem:def test_task_creation(self):# 测试任务创建功能passdef test_task_assignment(self):# 测试任务分配功能pass# 模拟运行所有测试
def run_all_tests():test1 = TestProjectManagementSystem()test1.test_task_creation()test1.test_task_assignment()# 假设还有其他测试用例,依次运行# 签入前调用运行所有测试的函数
if __name__ == "__main__":run_all_tests()# 只有当所有测试都通过后,才进行代码签入操作

在这个示例中,run_all_tests函数模拟了运行项目管理系统所有测试用例的过程,只有当所有测试都通过后,才符合干净签入的要求。

总结

测试驱动开发模式中的这些具体测试技术,为开发者提供了丰富的测试手段,帮助我们在软件开发过程中更全面、深入地进行测试。从子测试的规模控制到模拟对象的资源替代,从自分流的交互测试到日志字符串的消息记录,再到崩溃测试模拟、中断的测试以及干净签入等,每一种技术都在不同场景下发挥着重要作用。通过合理运用这些技术,我们能够提高测试的效率和质量,确保软件系统的稳定性和可靠性,为软件开发的成功奠定坚实基础。在实际开发中,开发者应根据项目需求和特点,灵活选择和组合这些测试技术,不断优化测试流程,提升软件的整体质量。

相关文章:

第27章 测试驱动开发模式:深入测试技术

写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…...

基于模糊PID的孵化箱温度控制系统(论文+源码)

1系统方案设计 本课题为基于模糊PID的孵化箱温度控制系统,其以STM32最小系统与模糊PID控制器为控制核心。系统主要包括数据采集模块、处理器模块、电机控制模块。 数据采集模块由温度传感器构成,通过温度传感器感应温度变化,获得待处理的数据…...

机器学习-数据集划分

文章目录 一. 为什么要划分数据集二. 数据集划分的方法1. 留出法:2. 交叉验证:将数据集划分为训练集,验证集,测试集3. 留一法:4. 自助法: 一. 为什么要划分数据集 为了能够评估模型的泛化能力,可…...

C++ 可变函数和参数

写一个函数&#xff0c;函数的参数是函数对象及参数&#xff0c;功能和thread类的构造函数相同。 代码示例&#xff1a; #include <iostream>#include <thread>#include <functional> using namespace std;void show0() { // 普通函数。cout <&…...

Hive之加载csv格式数据到hive

场景&#xff1a; 今天接了一个需求&#xff0c;将测试环境的hive数据导入到正式环境中。但是不需要整个流程的迁移&#xff0c;只需要迁移ads表 解决方案&#xff1a; 拿到这个需求首先想到两个方案&#xff1a; 1、将数据通过insert into语句导出&#xff0c;然后运行脚本 …...

C# OpenCV机器视觉:利用CNN实现快速模板匹配

在一个阳光灿烂的周末&#xff0c;阿强正瘫在沙发上&#xff0c;百无聊赖地换着电视频道。突然&#xff0c;一则新闻吸引了他的注意&#xff1a;某博物馆里一幅珍贵的古画离奇失踪&#xff0c;警方怀疑是被一伙狡猾的盗贼偷走了&#xff0c;现场只留下一些模糊不清的监控画面&a…...

【MCAL实战】MCU模块配置实践

目录 前言 正文 1.硬件分析 1.1 MCU系统模式分析 1.2MCU晶振使用分析 2.MCU通用配置 2.1 McuGeneralConfiguration 2.2 McuModuleConfiguration 2.3 McuResetSettingConf 2.4 McuTrapSettingConf 2.4 其他 3.MCU模式配置 3.1 McuModeSettingConf_0 3.2 McuModeSe…...

Vue3笔记——(三)hooks、路由

015 hooks 作用&#xff1a;使得代码更加模块化和可维护 Person.vue <template><div><h2>当前求和{{ sum }}</h2><button click"addFn">点我sum1</button></div> </template> <script setup lang"ts"…...

WinRAR.exe命令行的使用

工具 命令行打包命令 rem 默认压缩根目录&#xff0c;递归处理子文件夹使用 -r WinRAR.exe a -r test.rar C:/web/Views/...

【fly-iot飞凡物联】(20):2025年总体规划,把物联网整套技术方案和实现并落地,完成项目开发和课程录制。

前言 fly-iot飞凡物联专栏&#xff1a; https://blog.csdn.net/freewebsys/category_12219758.html 1&#xff0c;开源项目地址进行项目开发 https://gitee.com/fly-iot/fly-iot-platform 完成项目开发&#xff0c;接口开发。 把相关内容总结成文档&#xff0c;并录制课程。…...

Effective C++ 规则51:编写 new 和 delete 时需固守常规

1、背景 在 C 中&#xff0c;如果你需要为类自定义 new 和 delete&#xff0c;必须遵循一些约定和规则&#xff0c;以确保内存管理的一致性、可维护性和安全性。当我们使用 new 和 delete 操作时&#xff0c;C 编译器会&#xff1a; 调用全局或类特定的 operator new 来分配内…...

【更正版】梯级水光互补系统最大化可消纳电量期望短期优化调度模型

目录 1 主要内容 目标函数&#xff1a; 约束条件&#xff1a; 线性化处理&#xff1a; 流程示意&#xff1a; 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《梯级水光互补系统最大化可消纳电量期望短期优化调度模型》&#xff0c;构建了以最大化整体可…...

移动端VR处理器和传统显卡的不同

骁龙 XR 系列芯片 更多地依赖 AI 技术 来优化渲染过程&#xff0c;而传统的 GPU 渲染 则倾向于在低画质下运行以减少负载。这种设计是为了在有限的硬件资源下&#xff08;如移动端 XR 设备&#xff09;实现高性能和低功耗的平衡。以下是具体的分析&#xff1a; 1. AI 驱动的渲染…...

基于回归分析法的光伏发电系统最大功率计算simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于回归分析法的光伏发电系统最大功率计算simulink建模与仿真。选择回归法进行最大功率点的追踪&#xff0c;使用光强和温度作为影响因素&#xff0c;电压作为输出进行建模。…...

JVM深入学习(一)

目录 一.JVM概述 1.1 为什么要学jvm&#xff1f; 1.2 jvm的作用 1.3 jvm内部构造 二.JVM类加载 2.1类加载过程 2.2类加载器 2.3类加载器的分类 2.4双亲委派机制 三.运行时数据区 堆空间区域划分&#xff08;堆&#xff09; 为什么分区(代)&#xff1f;&#xff08…...

【精选】基于数据挖掘的招聘信息分析与市场需求预测系统 职位分析、求职者趋势分析 职位匹配、人才趋势、市场需求分析数据挖掘技术 职位需求分析、人才市场趋势预测

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…...

视觉语言模型 (VLMs):跨模态智能的探索

文章目录 一. VLMs 的重要性与挑战&#xff1a;连接视觉与语言的桥梁 &#x1f309;二. VLMs 的核心训练范式&#xff1a;四种主流策略 &#x1f5fa;️1. 对比训练 (Contrastive Training)&#xff1a;拉近正例&#xff0c;推远负例 ⚖️2. 掩码方法 (Masking)&#xff1a;重构…...

kafka消费者详细介绍(超级详细)

文章目录 一、Kafka 消费者与消费者组1.1 Kafka 消费者&#xff08;Consumer&#xff09;概述1.1.1 消费者工作流程1.1.2 消费者的关键配置 1.2 Kafka 消费者组&#xff08;Consumer Group&#xff09;概述1.2.1 消费者组的工作原理1.2.2 消费者组的优点1.2.3 消费者组的再均衡…...

CF 339A.Helpful Maths(Java实现)

题目分析 输入一串式子&#xff0c;输出从小到大排列的式子 思路分析 如上所说核心思路&#xff0c;但是我要使用笨方法&#xff0c;输入一串式子用split分割开&#xff0c;但是此时需要用到转义字符&#xff0c;即函数内参数不能直接使用“”&#xff0c;而是“\\”。分割开后…...

web前端3--css

注意&#xff08;本文一切代码一律是在vscode中书写&#xff09; 1、书写位置 1、行内样式 //<标签名 style"样式声明"> <p style"color: red;">666</p> 2、内嵌样式 1、style标签 里面写css代码 css与html之间分离 2、css属性:值…...

Java Web-Request与Response

在 Java Web 开发中&#xff0c;Request 和 Response 是两个非常重要的对象&#xff0c;用于在客户端和服务器之间进行请求和响应的处理&#xff0c;以下是详细介绍&#xff1a; Request&#xff08;请求对象&#xff09; Request继承体系 在 Java Web 开发中&#xff0c;通…...

Spring AOP通知类型全解析:掌握方法执行前后的艺术

Spring的通知&#xff08;Advice&#xff09;类型主要有以下几种&#xff0c;它们都是在方法执行的不同阶段进行拦截和处理的一种机制&#xff1a; 1. 前置通知&#xff08;Before Advice&#xff09;&#xff1a;在目标方法执行之前执行的通知。就像你吃饭前要先洗手一样&…...

(一)HTTP协议 :请求与响应

前言 爬虫需要基础知识&#xff0c;HTTP协议只是个开始&#xff0c;除此之外还有很多&#xff0c;我们慢慢来记录。 今天的HTTP协议&#xff0c;会有助于我们更好的了解网络。 一、什么是HTTP协议 &#xff08;1&#xff09;定义 HTTP&#xff08;超文本传输协议&#xff…...

未初始化数据恢复全攻略

没有初始化概述 在日常使用电脑、硬盘、U盘等存储设备时&#xff0c;我们可能会遇到“没有初始化”的提示。这一情况通常发生在存储设备突然无法被系统正常识别或访问时&#xff0c;系统往往要求我们先进行初始化操作。然而&#xff0c;初始化操作意味着对存储设备进行格式化&…...

学习数据结构(1)算法复杂度

1.数据结构和算法 &#xff08;1&#xff09;数据结构是计算机存储、组织数据的方式&#xff0c;指相互之间存在⼀种或多种特定关系的数据元素的集合 &#xff08;2&#xff09;算法就是定义良好的计算过程&#xff0c;取一个或一组的值为输入&#xff0c;并产生出一个或一组…...

Github 2025-01-25Rust开源项目日报Top10

根据Github Trendings的统计,今日(2025-01-25统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Python项目1Vue项目1JavaScript项目1Deno: 现代JavaScript和TypeScript运行时 创建周期:2118 天开发语言:Rust, JavaScript协议类型…...

免费GPU算力,不花钱部署DeepSeek-R1

在人工智能和大模型技术飞速发展的今天&#xff0c;越来越多的开发者和研究者希望能够亲自体验和微调大模型&#xff0c;以便更好地理解和应用这些先进的技术。然而&#xff0c;高昂的GPU算力成本往往成为了阻碍大家探索的瓶颈。幸运的是&#xff0c;腾讯云Cloud Studio提供了免…...

積分方程與簡單的泛函分析7.希爾伯特-施密特定理

1)def函數叫作"由核生成的(有源的)" 定义: 设 是定义在区域上的核函数。 对于函数,若存在函数使得, 则称函数是“由核生成的(有源的)”。 这里的直观理解是: 函数的“来源”可以通过核函数 与另一个函数的积分运算得到。 在积分方程理论中,这种表述常…...

2025年PHP面试宝典,技术总结。

面试是进入职场的第一道坎&#xff0c;因为我本身学校太一般的问题在面试中遇到了各种不爽&#xff0c;和那些高学历的相比自己真是信心大跌。我面试的方向是php开发工程师&#xff0c;主要做网站后台、APP接口等。下面是我这段时间总结的面试方面的常考常问的知识点&#xff0…...

网易Android开发面试题200道及参考答案 (上)

分析 Error 和 Exception 的区别 在 Java 编程中,Error 和 Exception 都继承自 Throwable 类,它们用于处理程序运行时出现的异常情况,但两者存在显著区别。 Error 通常表示系统级别的错误,是 Java 虚拟机(JVM)无法处理的严重问题,比如 OutOfMemoryError (内存溢出错误)…...