- 文档
- 教程
【免费下载链接】Python-100-Days
Python - 100天从新手到大师
本指南围绕 Day91-100/96.软件测试和自动化测试.md 展开,系统讲解软件测试的基本方法、测试阶段划分、测试驱动开发理念,以及 Python 生态下的单元测试与 UI、接口、性能自动化测试方案,并给出可直接运行的代码示例。读完本文,你将掌握使用 unittest/pytest 编写单元测试、使用 Selenium 驱动浏览器做 Web 自动化、通过 requests/HttpRunner 做接口测试的完整思路,并理解这些技术在 Python-100-Days 项目源码(如 Day31-35/code 下的测试用例)与 Django 商业项目(Day91-100/95.使用Django开发商业项目.md)中的落地方式。
软件测试概述
软件测试是一种用来促进鉴定软件的正确性、完整性、安全性和品质的过程,也就是在规定的条件下对程序进行操作以发现程序中的错误,衡量软件的品质并对其是否能满足设计要求进行评估的过程。测试的本质是"带着找错的目的去执行程序",因此测试用例设计的质量直接决定了测试的有效性。
测试的方法:黑盒测试与白盒测试
黑盒测试:测试应用程序的功能,而不是其内部结构或运作。测试者不需具备应用程序的代码、内部结构和编程语言的专门知识,只需知道系统"应该做什么"——即当键入一个特定的输入,可得到一定的输出。测试用例依据应用系统应该做的功能,按照规范、规格或要求设计,测试者选择有效输入和无效输入来验证是否产生正确的输出。此方法适用于大部分的软件测试,例如集成测试和系统测试。
白盒测试:测试应用程序的内部结构或运作,而不是测试应用程序的功能(即黑箱测试)。在白盒测试时,以编程语言的角度来设计测试用例,测试者输入数据以验证数据流在程序中的流动路径,并确定适当的输出,类似测试电路中的节点。
由于时间和成本的约束,软件测试中一个最为关键的问题是:"在所有可能的测试用例中,哪个子集能发现最多的错误?"。因此在设计测试用例时,白盒测试看重程序逻辑覆盖的程度(语句覆盖、条件覆盖、分支覆盖),黑盒测试则可以使用等价类划分、边界值分析、因果图分析、错误猜测等方法来设计测试用例。
在 Python-100-Days 项目中,Day31-35/code/test_example01.py 的文件头注释对这两种方法给出了清晰界定:白盒测试是"程序自己写的测试"(开发人员对代码内部逻辑的了解),黑盒测试则由"测试人员或 QA,不知道代码实现细节,只关注功能"来执行。
测试的种类(阶段)
- 单元测试:对软件组成单元进行测试,目的是检验软件基本组成单位的正确性,测试对象是软件设计的最小单位——函数。
- 集成测试:将程序模块采用适当的集成策略组装起来,对系统的接口及集成后的功能进行正确性检测。主要目的是检查软件单位之间的接口是否正确,集成测试的对象是已经经过单元测试的模块。
- 系统测试:主要包括功能测试、界面测试、可靠性测试、易用性测试、性能测试。
- 回归测试:为了检测代码修改而引入的错误所进行的测试活动,是软件维护阶段的重要工作。有研究表明,回归测试带来的耗费占软件生命周期的 1/3 总费用以上。
测试驱动开发(敏捷测试)
测试驱动开发(TDD)包括以下三个步骤:
- 为未实现的新功能或者改进编写自动化测试。
- 提供通过所有定义的测试的最小代码量。
- 重构代码以满足所需的质量标准。
测试驱动开发的好处在于可以有效防止软件回归,并提供更有质量的代码。此外,验收测试应该由客户来进行——客户通过对使用场景设计验收测试,对应用程序是否满足他们的要求进行客观、公正的确认。能够通过单元测试、甚至是系统测试的功能,未必能够通过客户的验收测试。
互联网应用和移动应用的测试
互联网应用的测试策略:
- 表示层测试(内容测试、站点结构测试、用户环境(浏览器、操作系统等))
- 业务层测试(性能、数据验证、事务、外部服务)
- 持久层测试(响应时间、数据完整性、容错性)
移动应用的测试策略:
- 真机测试
- 基于模拟器的测试
单元(模块)测试:unittest 与 pytest 实战
Python 的标准库里有为编写单元测试而准备的unittest模块,执行测试时建议使用 pytest 或 nose2。pytest 是一款能够自动搜索并执行测试的测试执行工具,并且会输出详细的错误报告。
仓库源码中的 unittest 实战
Day31-35/code/test_example01.py 是项目中针对查找算法的单元测试用例,它完整演示了 Python 标准库unittest的标准写法:
from unittest import TestCase from example01 import seq_search, bin_search class TestExample01(TestCase): """测试查找函数的测试用例""" # 执行每个测试函数之前要执行的方法 def setUp(self): self.data1 = [35, 97, 12, 68, 55, 73, 81, 40] self.data2 = [12, 35, 40, 55, 68, 73, 81, 97] # 执行每个测试函数之后要执行的方法 def tearDown(self): pass def test_seq_search(self): """测试顺序查找""" self.assertEqual(0, seq_search(self.data1, 35)) self.assertEqual(2, seq_search(self.data1, 12)) self.assertEqual(6, seq_search(self.data1, 81)) self.assertEqual(7, seq_search(self.data1, 40)) self.assertEqual(-1, seq_search(self.data1, 99)) self.assertEqual(-1, seq_search(self.data1, 7)) def test_bin_search(self): """测试二分查找""" self.assertEqual(1, bin_search(self.data2, 35)) self.assertEqual(0, bin_search(self.data2, 12)) self.assertEqual(6, bin_search(self.data2, 81)) self.assertEqual(2, bin_search(self.data2, 40)) self.assertEqual(7, bin_search(self.data2, 97)) self.assertEqual(-1, bin_search(self.data2, 7)) self.assertEqual(-1, bin_search(self.data2, 99))这个用例有几个值得注意的要点:
- 继承
TestCase、测试方法以test_开头:这是 unittest 约定俗成的命名规则,也是测试执行器自动发现测试用例的依据。 setUp/tearDown测试固件:setUp在每个测试函数执行前运行,用来准备测试数据(这里准备了乱序列表data1供顺序查找、有序列表data2供二分查找);tearDown在每个测试函数执行后运行,用于清理资源。这正是 Day91-100/95.使用Django开发商业项目.md 中提到的"测试固件(Fixture)——每次测试时都要使用的东西"。- 断言驱动验证:
assertEqual(期望值, 实际值)是 unittest 中最常用的断言。注意被测试的 example01.py 中,seq_search返回元素在列表中的下标(找不到返回 -1),bin_search同样返回下标,因此断言中第一个参数是期望的下标值。这里也隐含了白盒测试的痕迹:测试者需要知道查找函数"返回下标"这一实现约定才能写出断言。
再来看排序算法的测试用例 Day31-35/code/test_example02.py:
from unittest import TestCase from example02 import select_sort, merge class TestExample02(TestCase): """测试排序函数的测试用例""" def setUp(self): self.data1 = [35, 97, 12, 68, 55, 73, 81, 40] self.items1 = [12, 35, 68, 97] self.items2 = [40, 55, 73, 81] def test_merge(self): items = merge(self.items1, self.items2) for i in range(len(items) - 1): self.assertLessEqual(items[i], items[i + 1]) def test_select_sort(self): """测试顺序查找""" items = select_sort(self.data1) for i in range(len(items) - 1): self.assertLessEqual(items[i], items[i + 1])这里用到了另一个断言方法assertLessEqual(a, b)(断言a <= b)。排序算法的验证思路与查找不同:不必穷举所有排列组合,而是验证"结果序列中任意相邻两元素都满足非递减关系"这一排序的本质属性。这也说明了测试用例设计的精髓——针对被测函数的核心不变量设计断言,而不是机械地对拍输出。
执行单元测试的几种方式
执行上述测试用例有两条标准路径(见 test_example01.py 文件头注释):
- 在测试文件末尾加入
if __name__ == '__main__': unittest.main(),直接运行该文件; - 使用标准库的测试运行器:
python3 -m unittest test_example01.py。
执行结果示例(Django 商业项目文档中的实测输出)为:
Ran 7 tests in 0.002s OK第三方测试工具链
pytest / nose2:pytest 能够自动搜索并执行测试(自动发现符合test_*命名规则的函数和类),输出详细的错误报告;nose2 是 nose 的继任者,同样提供自动发现和更友好的输出。安装与使用命令如下:
pip install pytest pytest-cov pytest -v --cov pip install nose2 cov-core nose2 -v -Ctestfixtures:该库整合了多种典型配置器,提供了生成目录、更改系统日期、生成 mock 对象的功能模块,这些模块能够帮助我们将单元测试与单元测试所依赖的环境分离开。
mock:mock 是将测试对象所依赖的对象替换为虚拟对象的库。在测试的时候,我们可以为虚拟对象指定其在被调用时的返回值以及是否发生异常等。这对应着 Day91-100/95.使用Django开发商业项目.md 中"孤立外部依赖"的思路——在测试过程中需要隔离数据库、外部接口调用、时间依赖等,手段包括数据源本地化/置于内存中/测试之后回滚,以及使用 stub 和 mock 替代真实的外部服务。
tox:tox 能便捷地为我们准备好执行测试所需的环境。tox 会在多个 virtualenv 环境中搭建测试环境,然后在这些环境中执行测试并显示结果。它能够把测试工具的选项及环境变量等内容统一起来,所以我们只需执行tox命令即能轻松完成所需的测试——这正是持续集成中"一处配置、多环境验证"的经典工具。
测试覆盖率
单元测试不仅要能跑通,还需要评估"测了多少"。pytest-cov 或 cov-core 可以对测试覆盖度进行评估,覆盖率用百分比表示:比如测试代码执行过了程序的每一行,那么覆盖率就是 100%。此时几乎不会出现新程序上线后突然无法运行的尴尬情况。但需要注意,覆盖率只检查"测试代码不足、测试存在疏漏",它不关心代码内容究竟是什么,"测试内容是否妥当"并不归它管。在 Day91-100/95.使用Django开发商业项目.md 中给出了实测的覆盖率报告示例:
Name Stmts Miss Cover --------------------------------------------- test_ddt_example.py 18 0 100% test_pytest_example.py 11 6 45% test_unittest_example.py 22 0 100%自动化测试
自动化测试的核心思想是"把手工操作变成脚本操作"。按照被测试对象的形态,可以分为 UI 自动化测试、接口自动化测试和其他领域的自动化测试。
UI 自动化测试
桌面端:PyAutoGUI
PyAutoGUI 是桌面端的 UI 自动化工具,它可以模拟鼠标移动、点击、拖拽以及键盘输入,适合对桌面应用程序进行自动化操作。在爬虫和自动化运维场景中,它也可以用来处理"需要人工点击"的杂活。需要注意的是,PyAutoGUI 依赖屏幕坐标定位,脚本对屏幕分辨率和窗口布局较为敏感。
移动端:Appium
Appium 是移动端(iOS / Android)的自动化测试框架,它允许使用 WebDriver 协议驱动手机 App。与 Selenium 类似,Appium 支持多种编程语言编写测试脚本,并且支持真机与模拟器两种测试环境,对应原文档中"移动应用测试策略"里的真机测试与基于模拟器的测试。
Web 端:Selenium
Selenium 是实现 Web 应用程序的功能测试以及集成测试自动化的浏览器驱动测试工具群。和使用浏览器的用户相同,Selenium 可以在浏览器进行鼠标操作、在表单中输入文字、验证表单的值等,利用这一点就可以将手动操作变成自动化操作。在 Day61-65/64.使用Selenium抓取网页动态内容.md 中,Selenium 还被用于抓取使用 JavaScript 动态渲染的网页内容——约四分之三的网站内容或部分内容是通过 JavaScript 动态生成的,查看网页源代码无法获得这些内容,而 Selenium 可以驱动真实浏览器拿到渲染后的完整 DOM。
Selenium 的优点:
- 自动化测试用例制作简单:Selenium 提供了 Selenium IDE 工具,该工具可以捕获鼠标、键盘的操作,然后通过重放功能来重复这些操作,从而简单制作测试用例。
- 支持多种浏览器和操作系统。
Selenium 的组件:
- Selenium IDE:录制/回放测试用例的浏览器插件。
- Selenium Remote Control(现已并入 WebDriver):通过 HTTP 与浏览器通信的服务端组件。
- Selenium WebDriver:以编程方式驱动浏览器的核心 API,是当前主流的 Selenium 使用方式。
WebDriver 的基本用法(以 Chrome 为例,代码见 64.使用Selenium抓取网页动态内容.md):
from selenium import webdriver from selenium.webdriver.common.by import By browser = webdriver.Chrome() browser.get('https://www.baidu.com/') # 通过元素ID获取元素 kw_input = browser.find_element(By.ID, 'kw') # 模拟用户输入行为 kw_input.send_keys('Python') # 通过CSS选择器获取元素 su_button = browser.find_element(By.CSS_SELECTOR, '#su') # 模拟用户点击行为 su_button.click()如果出现WebDriverException: Message: 'chromedriver' executable needs to be in PATH错误,说明 Chrome 浏览器驱动未加入 PATH 环境变量,有三种解决办法:
- 将下载的 ChromeDriver 放到已有的 PATH 环境变量下(建议与 Python 解释器放在同一目录);
- 将 ChromeDriver 放到项目虚拟环境下的
bin(Windows 为Scripts)文件夹中; - 在创建浏览器对象时通过
Service参数显式指定驱动路径:
from selenium import webdriver from selenium.webdriver.chrome.service import Service browser = webdriver.Chrome(service=Service(executable_path='venv/bin/chromedriver'))动态元素的等待策略:网页元素可能是动态渲染的,立即查找可能引发NoSuchElementException。Selenium 提供了两种等待方式:
- 隐式等待:
browser.implicitly_wait(10),设置全局等待时间,让浏览器完成对页面元素的渲染后再查找; - 显式等待:创建
WebDriverWait对象并设置等待条件(expected_conditions),条件满足后再继续操作:
from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait wait_obj = WebDriverWait(browser, 10) wait_obj.until( expected_conditions.presence_of_element_located( (By.CSS_SELECTOR, '#content_left') ) )常用等待条件包括:title_is / title_contains(标题是指定内容/包含指定内容)、visibility_of(元素可见)、presence_of_element_located(定位的元素加载完成)、visibility_of_element_located(定位的元素变得可见)、element_to_be_clickable(元素可点击)、alert_is_present(出现 Alert 弹窗)等。
执行 JavaScript:对瀑布式加载的页面,可通过browser.execute_script('document.documentElement.scrollTop = document.documentElement.scrollHeight')将网页滚到最下方,加载更多内容。
无头浏览器:不需要看到浏览器窗口时,通过options.add_argument('--headless')设置无头模式运行,适合 CI 环境。
与持续集成工具协作:持续集成指的是频繁地将代码集成到主干,其好处主要有两个:
- 快速发现错误:每完成一点更新就集成到主干,可以快速发现错误,定位错误也比较容易;
- 防止分支大幅偏离主干:如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。
持续集成的目的就是让产品可以快速迭代,同时保持高质量。它的核心措施是:代码集成到主干之前,必须通过自动化测试,只要有一个测试用例失败,就不能集成。编程大师 Martin Fowler 曾经说过:"持续集成并不能消除 Bug,而是让它们非常容易发现和改正。"
可以在 Jenkins 中安装 "Seleniumhq Plugin" 插件,将 Selenium IDE 制作的测试用例保存为 HTML 格式并提供给 Jenkins 使用,基本步骤是:
- 在执行测试的机器上,从版本控制系统中下载测试套件和测试用例;
- 在执行测试的机器上下载 Selenium Server;
- 从 Jenkins 的"系统管理"中选择"插件管理"来安装 "Seleniumhq Plugin";
- 在 Jenkins 的"系统管理"中选择"系统设置"并配置 "Selenium Remote Control" 下的 "HTMLSuite Runner";
- 新建测试用的 Jenkins 任务并进行配置,配置内容包括:浏览器、起始 URL、测试套件和测试结果输出文件。
配置完成后,就可以执行 Jenkins 的"立即构建"了。
其他 Web 端测试选择:
- WebTest:可以对 WSGI 应用执行模拟请求并获取结果,基本上所有 WSGI 应用的测试都可以用它,非常适合测试 Django、Flask 等框架编写的应用;
- Splinter:对 Selenium 的二次封装,使用上更加方便简单;
- Robot Framework:基于关键字的通用自动化测试框架,在 Day91-100/95.使用Django开发商业项目.md 中同样被列为 Web 自动化测试工具,其典型用法是配合 SeleniumLibrary 以关键字驱动的方式编写可读性高的测试套件。
接口自动化测试
接口测试关注的是后端 API 的正确性,相比 UI 测试更稳定、执行更快,是自动化测试中性价比最高的部分。常见方案:
- requests:Python 最流行的 HTTP 客户端库,可以手工编写脚本来验证接口的响应状态码、响应体结构、业务字段等,也可以作为其他接口测试框架的底层依赖。
- HttpRunner:基于 YAML/JSON 描述的接口测试框架,支持参数化、数据驱动、测试用例分层(用例-套件-项目),并能输出漂亮的测试报告,适合团队级的接口回归。
- PyRestTest:面向 RESTful 服务的测试框架,通过配置文件声明式地定义请求与预期结果。
对应地,在 Day91-100/95.使用Django开发商业项目.md 中可以看到 Django 项目内置的测试能力:Django 的TestCase扩展了unittest.TestCase,绑定了一个名为client的属性,可以用来模拟浏览器发出的 GET、POST、DELETE、PUT 等请求;djangorestframework 则提供了基于 Bootstrap 定制的页面来显示接口返回的 JSON 数据,也可以使用 PostMan 这样的工具对 API 接口进行测试。
其他方面的自动化测试
- Locust:开源的负载测试工具,使用 Python 编写压测脚本,以协程方式模拟大量并发用户,实时输出性能指标;
- pythem:集成了网络侦查、漏洞利用等多类功能的渗透测试框架。
测试相关工具
在真实项目中,除了自动化测试框架外,还经常用到以下测试与性能验证工具:
| 工具 | 定位 |
|---|---|
| PostMan | 接口调试与接口测试,支持集合(Collection)管理和自动化运行 |
| AB(ApacheBench) | 命令行 HTTP 压测工具,可快速验证单机 QPS 与响应时间 |
| JMeter | 开源的压力测试与性能测试工具,支持图形化界面和多种协议 |
| LoadRunner | 商业级性能测试工具,支持大规模虚拟用户模拟 |
| Benchmark Factory | 数据库与应用的基准测试工具 |
| WAS(Web Application Stress Tool) | 微软提供的 Web 应用压力测试工具 |
总结
从 96.软件测试和自动化测试.md 出发,我们可以梳理出一条完整的学习与实践路径:先理解黑盒/白盒两种测试方法背后的用例设计哲学(等价类划分、边界值、逻辑覆盖),再按单元测试→集成测试→系统测试→回归测试的阶段组织测试活动;在 Python 工程中,用 unittest 写测试用例、用 pytest/nose2 执行与报告、用 testfixtures/mock 隔离外部依赖、用 tox 统一多环境验证,是标准的单元测试实践(可对照 Day31-35/code/test_example01.py 与 Day31-35/code/test_example02.py 两个真实用例);在系统层面,用 Selenium(Web)、Appium(移动端)、PyAutoGUI(桌面端)做 UI 自动化,用 requests/HttpRunner/PyRestTest 做接口自动化,用 Locust 做性能验证,再通过 Jenkins 等持续集成工具将自动化测试固化为"提交即验证"的质量门禁。进一步的 Django 工程化实践——包括数据驱动测试、覆盖率评估、Django TestCase 模拟请求等——可以继续阅读 Day91-100/95.使用Django开发商业项目.md 的"测试相关"章节。
- 文档
- 教程
【免费下载链接】Python-100-Days
Python - 100天从新手到大师
相关推荐
从新手到专家:Hermes WebUI会话管理完全攻略
从新手到专家:Hermes WebUI会话管理完全攻略 Hermes WebUI是一款功能强大的开源AI助手Web界面,它让您能够通过浏览器或手机轻松管理AI对
人工智能AI 应用AI Agent交互助手MCP 服务前端革命性图像分类模型mambaout_base_plus_rw.sw_e150_in12k:基于MambaOut架构的终极视觉解决方案
革命性图像分类模型mambaout_base_plus_rw.sw_e150_in12k:基于MambaOut架构的终极视觉解决方案 在计算机视觉领域, mam
Zcash 自动化单元测试指南:从 Boost/GoogleTest 单元测试到 Python RPC 回归测试
Zcash 自动化单元测试指南:从 Boost/GoogleTest 单元测试到 Python RPC 回归测试 本文是 Zcash 节点(zcashd)仓库中
区块链密码学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考