尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python Unittest框架详解:自动化测试用例组织、断言与报告生成实战

Python Unittest框架详解:自动化测试用例组织、断言与报告生成实战 1. 先搞清楚Unittest到底解决什么问题我在不同公司带过好几拨测试团队发现一个很普遍的现象很多人写自动化测试写到后面就变成了“脚本堆砌”——一个函数接一个函数跑通了就完事完全不管什么框架、什么规则。等到用例量上去几十上百条用例堆在同一个文件里执行顺序乱、数据互相污染、出错了根本定位不到是哪个用例挂的甚至跑完都不知道到底过了几条、挂了几条。这时候Unittest就派上用场了。Unittest是Python标准库自带的单元测试框架不需要额外安装。它解决的问题不是“怎么写一个测试步骤”而是如何让测试代码变得可组织、可执行、可统计、可维护。说直白点它帮你把“验证某个功能对不对”这件事从零散的脚本变成了一套有结构、有执行器、有结果反馈的工程体系。自动化测试在Python圈子里常见的套路有很多有人用pytest有人用nose也有人自己写脚本硬跑。但Unittest有个天然优势它是标准库的一部分只要你装了Python它就在那不会因为环境问题导致测试框架跑不起来。对于团队协作、CI集成、新人上手来说这非常重要。而且Unittest的设计思想继承自JUnit如果你之后接触Java、C#的测试框架迁移成本极低。所以这篇内容适合谁适合刚入门Python自动化测试的测试工程师也适合写了一段时间脚本、但想让自己的用例更规范的开发。我会把Unittest的核心机制、编写规范、批量执行、报告生成以及一套完整的自动化测试落地流程全部串起来讲一遍。很多细节是我实际项目里踩过坑之后总结出来的常规教程里不会写这么细。1.1 自动化测试不止是“跑通脚本”这么简单我记得刚入行那会儿带我的老大跟我说过一句话“脚本跑通了只是你完成了任务的10%剩下90%是让别人能看懂、能复用、能维护。”这句话我一直记到现在。自动化测试的核心价值不在于你写了多少行代码而在于这套东西能不能持续运行、能不能快速反馈、能不能在业务变动时低成本维护。Unittest这一层框架恰恰就是在解决“可维护”和“可反馈”这两个问题。比如当你面对一套登录接口需要验证正常登录、密码错误、账号不存在、验证码失效、账号锁定等十几个场景时如果每个场景都写成独立脚本那维护成本会爆炸。而用Unittest组织每个场景是一个独立的test方法它们共享同一套setUp初始化逻辑执行完之后还能自动统计通过率。这种结构化的收益用例越多越明显。1.2 一个最小可跑的Unittest用例长什么样先看一个最简单的例子感受一下Unittest的基本形态import unittest class TestMathFunc(unittest.TestCase): def test_add(self): self.assertEqual(1 1, 2) def test_subtract(self): self.assertEqual(3 - 1, 2) if __name__ __main__: unittest.main()用命令行执行python test_math.py运行后控制台会输出.. ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK这段代码里有几个关键点TestMathFunc继承了unittest.TestCase这是所有测试类的父类以test_开头的方法会被自动识别为测试用例assertEqual(a, b)用来判断两个值是否相等unittest.main()是入口负责扫描当前模块中所有继承TestCase的类并执行其中的测试方法。看起来简单但底层逻辑值得琢磨。Unittest替你做了哪些事它会扫描模块、识别用例、按规则执行、捕获异常、统计结果。你要做的只是按照它的规则去写测试代码。这其实就是框架的意义——约束规范解放精力。1.3 Unittest的四个核心部件TestCase、TestSuite、TestRunner、TestFixtureUnittest的整体架构由四个核心部件组成理解了这四个东西整个框架就等于拿下了。TestCase测试用例所有测试类的基类每个test_开头的方法就是一条独立的测试用例。它是最小的测试执行单元。TestSuite测试套件用来把多条用例聚合在一起解决多文件、多模块的用例组织问题。你可以把TestSuite理解成一个“测试集装箱”里面既可以放TestCase也可以嵌套其他的TestSuite。TestRunner测试执行器负责执行测试套件并输出结果。最常用的是TextTestRunner它的作用就是驱动整个测试流程收集结果然后打印到控制台。我们后面要讲的HTMLTestRunner本质上也只是一个扩展版的Runner。TestFixture测试夹具也叫测试固件包括setUp()、tearDown()、setUpClass()、tearDownClass()这四个方法用来做测试前的准备和测试后的清理。比如连接数据库、初始化测试数据、打开浏览器这些操作都可以放在fixture里。把这四个部件串起来整个执行流程就是TestRunner拿到TestSuiteTestSuite里装着TestCase执行每个TestCase的前后自动调用fixture最后汇总结果输出。理解了这个链路后面所有的设计都是在这个骨架上添砖加瓦。2. 用例编写规范与断言体系写测试前必须理清的事我带新人时发现多数人写测试代码最大的问题不是不会写而是“太随意”。函数名随意起、断言随意用、数据随意写看起来跑通了实际上问题很大。所以这一节我重点讲Unittest的编写规范以及它的断言体系怎么用才到位。2.1 命名规则为什么必须是test开头Unittest通过unittest.TestLoader加载用例时默认的模式是匹配test*.py文件然后加载其中继承TestCase的类再通过getTestCaseNames方法获取以test开头的方法。如果你不按这个规则命名比如写了个check_login()Unittest会直接忽略它而且不报错。这就会造成一种很危险的情况你以为所有用例都跑了实际上漏了好几条。在项目里维护自动化测试最怕的不是用例挂了而是用例没被执行但你不知道。除了方法名文件命名也要注意。Unittest通过discover发现用例时默认匹配test*.py所以测试文件建议统一命名为test_xxx.py。命名清晰还有个额外好处在测试报告里能直接看到“TestLogin.test_login_success”这种完整路径出问题了一眼就能定位到代码位置。2.2 断言方法速查从assertEqual到assertRaisesUnittest的断言方法非常丰富掌握常用的这些基本就够了我整理了一张速查表断言方法含义使用场景assertEqual(a, b)判断a与b相等对比接口返回值、计算结果assertNotEqual(a, b)判断a与b不相等验证修改生效、数据隔离assertTrue(x)判断x为True验证布尔类型的条件assertFalse(x)判断x为False验证开关、状态被关闭assertIsNone(x)判断x为None验证空值返回assertIsNotNone(x)判断x不为None验证有数据返回assertIn(a, b)判断a在b中验证列表、字典、字符串包含关系assertNotIn(a, b)判断a不在b中验证排除某字段assertAlmostEqual(a, b)判断a约等于b浮点数计算金额、百分比、浮点运算assertRaises(exception)判断抛出指定异常验证非法输入、异常处理逻辑assertIsInstance(obj, class)判断obj是class的实例数据格式校验大部分断言都接受第三个参数msg用来写自定义的失败提示。我强烈建议在实际项目中加上这个参数尤其是在接口测试里self.assertEqual(resp.status_code, 200, 登录接口返回状态码异常实际值为{}.format(resp.status_code))失败时报告里直接显示具体信息比干巴巴的“AssertionError: 200 ! 500”好排查多了。2.3 测试固件setUp和tearDown什么时候用什么时候别用setUp()和tearDown()是每条用例执行前和执行后自动调用的方法。注意是每条用例。如果你有10条test方法那setUp和tearDown各被调用10次。什么时候该用当你每条用例都需要相同的准备和清理动作时。比如测试接口时需要生成一个唯一的测试数据编号每次执行前先清理上一次的脏数据比如UI自动化测试时每条用例都要重新打开浏览器并登录。把这些重复逻辑抽到setUp和tearDown里用例方法本身就会变得非常干净只保留业务操作和断言。这里有个反例要提醒一下如果setUp里的操作非常耗时比如初始化大型测试数据、启动Android模拟器那每跑一条用例就初始化一次整个测试周期会慢到让人崩溃。这种场景应该用setUpClass()和tearDownClass()。它们是类级别的整个测试类只执行一次适合放那些重量级的公共初始化。举个实际案例我做过一个App的自动化测试项目启动App的耗时大约20秒。如果放在setUp里跑30条用例光启动App就要10分钟。后来我把App启动逻辑移到setUpClass里全部用例只启动一次整个测试时间压缩到不到2分钟而且这个改动没有损失任何测试覆盖。代价是用例之间不能有状态依赖所有数据必须在业务层自己搭好。做取舍的时候要权衡清楚。3. 用例组织与批量执行从单文件到项目级测试套件当你的测试代码只有一两个文件时直接python test_xxx.py就够了。但一旦测试模块多起来比如按业务模块拆成了10个文件、50个文件就需要一套机制来统一调度。Unittest提供的答案就是TestSuite和discover。3.1 TestSuite手动组织适合什么场景TestSuite最常见的用法是手动添加用例然后交给Runner执行import unittest from test_login import TestLogin from test_order import TestOrder if __name__ __main__: suite unittest.TestSuite() suite.addTest(TestLogin(test_login_success)) suite.addTest(TestOrder(test_create_order)) runner unittest.TextTestRunner(verbosity2) runner.run(suite)这里addTest接收的是用例实例第一个参数是测试类第二个参数是方法名。用这种方式你可以精确控制执行哪些用例甚至控制用例的执行顺序。但这种方式的缺点也很明显每加一条用例都要修改这个“总入口”文件维护成本和文件规模成正比。所以我的建议是手动组织TestSuite只用在以下几个场景冒烟测试集只挑几条核心用例快速验证主流程通不通Bug回归集针对某个Bug把相关的几条用例挑出来单独跑需要强制顺序执行的场景。如果是为了跑整个项目的全部用例用discover更明智。3.2 discover自动发现用例项目级测试的正确打开方式unittest.TestLoader().discover()是项目级测试的标配。它做的事情是指定一个起始目录自动递归查找所有符合匹配模式默认test*.py的文件把其中所有测试用例全部加载进来。import unittest if __name__ __main__: loader unittest.TestLoader() suite loader.discover(start_dir./tests, patterntest_*.py) runner unittest.TextTestRunner(verbosity2) runner.run(suite)这段代码会扫描tests目录下的所有test_开头的.py文件加载全部用例。你每新增一个测试文件只要命名规范根本不用改任何代码用例就会被自动发现并执行。这就是“约定优于配置”的思想。我实际项目里的目录结构一般是这样的project/ ├── tests/ │ ├── __init__.py │ ├── test_login.py │ ├── test_order.py │ └── test_user.py ├── common/ │ ├── __init__.py │ ├── base_case.py │ └── request_util.py ├── run_all.py └── requirements.txtrun_all.py作为统一入口调用discover加载tests目录下所有用例。这个文件从写好之后基本就没再动过后续新增用例都只是往tests目录里加文件。3.3 执行顺序与依赖问题Unittest为什么默认不保证顺序很多新手会在网上搜“Unittest怎么控制执行顺序”原因就是默认执行顺序是按照方法名的ASCII码排序来的不是按你写的顺序。比如你写了test_1、test_2、test_10执行顺序实际上是test_1、test_10、test_2因为ASCII码比较中“10”排在“2”前面。如果你的用例编号写成两位数那排序就按字母顺序来这很容易让人困惑。我个人的建议是不要在测试用例之间建立顺序依赖。每条test方法都应该是独立的、可单独执行的。如果你发现必须让test_a先跑完test_b才能跑那说明设计有问题应该把这些操作合并到一条用例里或者放到测试数据准备层去处理。理由很简单一旦用例之间有依赖并行执行、断点续跑、单条调试全都做不了了而且出问题时排查成本成倍上升。我们做自动化测试的目的是节省时间不是给未来挖坑。4. 测试报告与控制台输出让结果能给别人看自动化测试跑完如果只是自己看一眼控制台那价值就缩水了一大半。真正到了项目里测试结果是要发给开发、发给你leader、甚至发给客户的。这时候一份结构清晰、可读性强的测试报告就是刚需。4.1 控制台输出到底在说什么先解释一下TextTestRunner输出里的几个字符F..E. ERROR: test_sub (__main__.TestMathFunc) ---------------------------------------------------------------------- Traceback (most recent call last): ... FAIL: test_add (__main__.TestMathFunc) ---------------------------------------------------------------------- AssertionError: 2 ! 3 ---------------------------------------------------------------------- Ran 5 tests in 0.003s FAILED (failures1, errors1).代表用例通过F代表断言失败也就是功能不符合预期E代表用例执行报错代码本身抛了异常。失败和错误在测试框架里是两个完全不同的概念。失败Failure是断言不通过说明被测功能有问题错误Error是用例代码异常说明测试代码本身有Bug。排查时永远先看Error因为测试代码有Bug结果根本不可信。verbosity参数控制日志详细程度verbosity0只显示最终结果verbosity1显示进度条verbosity2显示每条用例名。我建议平时跑直接用2信息量足够排查问题。4.2 生成HTML测试报告HTMLTestRunner使用与改进HTMLTestRunner是Unittest生态里最常用的第三方报告库。它基于标准库改造把测试结果渲染成HTML页面支持按模块折叠、统计通过率、展示错误堆栈非常直观。代码示例import unittest from HTMLTestRunner import HTMLTestRunner if __name__ __main__: suite unittest.TestLoader().discover(./tests, patterntest_*.py) with open(report.html, wb) as f: runner HTMLTestRunner( streamf, title登录模块自动化测试报告, description执行环境Python 3.9 / Chrome 114 ) runner.run(suite)这里有个细节HTMLTestRunner的stream必须是以二进制模式打开的文件对象不然会报TypeError。另外很多人下载到的HTMLTestRunner是Python2时代的版本直接用在Python3上会报ImportError: No module named StringIO。解决方法是下载兼容Python3的版本或者是自己改一下源码把import StringIO改成import io、StringIO.StringIO改成io.StringIO。如果项目里不想依赖这个第三方库也有个更省事的方法直接把TextTestRunner的结果重定向到文件生成纯文本日志。虽然不好看但内容完整配合tail -f看日志也很实用。4.3 报告处置细节时间戳、路径、失败截图报告文件最怕覆盖。如果每次执行都写到同一个文件名历史记录就全丢了。我习惯在生成报告时加上时间戳from datetime import datetime now datetime.now().strftime(%Y%m%d_%H%M%S) report_path freports/report_{now}.html还有一个细节UI自动化测试时如果用例失败最好能自动截图并附加到报告里。有些HTMLTestRunner的魔改版本支持把截图以base64形式嵌进HTML这个改造代码并不多。如果你暂时没法改造也可以先把截图存到固定的目录文件命名包含用例名和时间戳然后在报告的失败描述里打印截图路径。这样开发拿到报告后能直接去翻图体验比纯文字好非常多。5. 自动化测试实现流程从需求到落地的完整路径框架和语法讲完接下来是重头戏一套完整的自动化测试实现流程到底该怎么走。我这里讲的是“从零到上线”的全过程既有接口自动化也会穿插说明UI自动化和App自动化的差异点。5.1 阶段一需求分析与场景梳理很多人上手自动化测试的第一反应是“写脚本”但我建议先干一件事列场景清单。拿一个登录功能举例正常、逆向、异常的场景至少包括正确账号密码登录成功正确账号密码错误登录失败账号不存在密码为空账号被锁定验证码正确与错误登录失败次数的限制登录接口的响应时间和状态码。把场景列成一个Excel或Markdown表格标注优先级P0/P1/P2。P0是核心主流程必须自动化覆盖P1是重要业务分支资源允许就做P2是边缘场景量力而行。这个环节决定了你的自动化测试能覆盖什么、覆盖多少。我见过不少团队一上来就疯狂写用例结果全写在了登录页面上核心的下单流程反而没覆盖。问题的根源就是缺了场景梳理这一步。5.2 阶段二测试框架结构设计与公共层搭建场景梳理完之后不要急着写测试方法先搭框架。一个成熟的自动化测试项目我建议至少分四层公共层common/放通用的工具类比如数据库连接、HTTP请求封装、日志封装、配置文件读取。公共层有两条原则一是只提供能力不含业务逻辑二是所有方法必须写好函数注释和示例方便别人调用。测试数据层data/统一管理测试数据可以用JSON、YAML或Excel。接口自动化测试里经常用的一个方法是把每个用例的请求参数、期望结果、用例描述都定义在数据文件里由测试代码统一读取。这种“数据驱动”的做法让不懂代码的同事也能维护用例。测试用例层tests/每个模块一个文件夹每个业务场景一个测试文件。用例方法里只做三件事准备数据、调用被测功能、断言结果。执行与报告层run_all.py、reports/统一入口负责加载用例、执行、输出报告。我画过很多次这个四层结构核心目的只有一个让测试代码和业务代码一样具有良好的工程结构。测试代码写几天就扔的项目我也见过不少最后全都是边写边改、边改边乱然后整个推翻重来。与其这样不如一开始就按工程标准来。这个阶段还有一个重点选型。接口自动化测试用requests库登录态管理用unittest自带的fixture或者requests.Session。UI自动化测试Web端用seleniumApp端用appium。图像识别类的工具比如Sikuli的替代方案在特定场景有奇效但稳定性普遍一般我建议只在常规选择失效时再用。5.3 阶段三用例实现与数据驱动在框架搭好的基础上写用例本身就是体力活了但有些细节决定了用例质量。第一用例命名要“所见即所得”。test_login_success_by_correct_account比test_login_001强太多了。你想想看测试报告发出去开发看到的是“测试登录正确账号密码登录成功”谁愿意看“test_001”命名清晰光是沟通成本就能省下一大笔。第二数据驱动要落地不是说说而已。用unittest.skipIf做条件跳过用ddt.data做参数化如果你用了ddt库或者统一用数据文件配合循环执行。参数化的核心收益是一条用例逻辑覆盖多条测试数据新增数据只改文件、不改代码。第三日志要“全链路可追踪”。每个关键步骤都打日志请求参数、接口地址、响应结果、断言值全都要记录。日志打得多排错时就能救命。我见过太多人用例失败后一脸懵原因是日志里啥都没记。第四断言要写到位。接口测试里最常见的问题就是只断言状态码业务字段完全不管。一个接口即使返回200也有可能带着错误码和错误信息。正确做法是先断言状态码再断言业务码最后断言关键业务字段。def test_login_success(self): resp self.client.post(/api/login, json{username: admin, password: 123456}) self.assertEqual(resp.status_code, 200, HTTP状态码异常) self.assertEqual(resp.json().get(code), 0, 业务码异常) self.assertIsNotNone(resp.json().get(token), 登录成功后应返回token)5.4 阶段四持续集成与定时执行自动化测试的终极形态是无人值守。每天凌晨自动跑早上上班打开报告就能看到结果。实现方式很成熟就是CI那套东西。最常用的方案是Jenkins。新建一个自由风格的任务源码管理里配置代码仓库地址构建触发器选“定时构建”或者“轮询SCM”构建步骤里填python -m venv venv source venv/bin/activate pip install -r requirements.txt python run_all.py构建后操作里把HTML报告路径配置进去Jenkins可以直接嵌到构建结果页面展示。这样整个流程就闭环了代码变更后或定时触发执行自动跑一遍全量测试结果自动存档失败自动发邮件提醒。也有团队用GitLab CI或GitHub Actions思路是一样的只是在ci配置文件里写自动化流程即可。对于还没上CI的团队最原始的crontab python方案也能用关键是先让测试跑起来再把流程做完善。别等所有条件都成熟了再上自动化那样永远等不来。6. 常见问题与排查技巧实录最后这部分是老本行把我在实际项目里踩过、带人时见到的各种坑整理一下。每条都是真实经历遇到同款问题直接照方抓药就行。6.1 用例执行顺序错乱现象明明代码里test_a写在test_b前面执行时却是test_b先跑。原因Unittest按方法名的ASCII码排序不按书写顺序。解决如果必须控制顺序用TestSuite手动addTest按你想要的顺序逐个添加。更推荐的方式重新设计测试类让每个test方法独立彻底不需要顺序依赖。顺带提一句如果真的需要执行顺序来控制业务流程比如创建订单后才能支付更好的做法是把“创建订单”作为前置条件放到setUpClass里而不是把“创建订单”和“支付”写成两条用例。6.2 断言失败后想继续执行后续用例现象一个test方法里多个断言第一个断言挂了后面的全不执行。解决如果两个验证点没有依赖关系拆成两条独立的test方法即可。这也是最推荐的做法因为报告里能清楚看到哪个点挂了如果非要在一个方法里连续断言可以考虑assertTrue和assertIn组合通过一次断言验证多个条件self.assertTrue(resp.status_code 200 and resp.json().get(code) 0)但不建议这么写因为失败时不方便定位。宁可多写几条用例也不要硬塞在一个方法里。6.3 fixture异常导致用例结果不可信现象一条用例在setUp里因为环境问题报错了结果E导致这条用例跑了等于没跑。排查思路先看E是来自fixture还是来自test方法。如果是fixture的问题那是环境准备不充分不是被测功能的问题。解决在setUp里对公共初始化加异常处理初始化失败时跳过这条用例而不是报错。用self.skipTest即可def setUp(self): try: self.client create_http_client() except Exception as e: self.skipTest(HTTP客户端初始化失败跳过用例{}.format(e))这样在报告里显示为SSkip语义上更加准确。6.4 跨文件路径问题现象用例单独跑没问题用discover整体跑就报“文件找不到”。原因discover执行时当前工作目录和测试文件所在目录不一致相对路径失效。解决不要在测试代码里使用相对路径统一基于项目的根目录来构造import os BASE_DIR os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DATA_FILE os.path.join(BASE_DIR, data, login_data.json)__file__是当前文件路径往上翻两级就是项目根目录再用os.path.join拼接。这样无论从哪里发起执行路径都不会出错。6.5 用例之间互相影响现象所有用例单独跑都通过合在一起跑就有几条挂掉。排查步骤看挂掉的用例是否依赖了共享数据比如同一个测试账号。如果前面的用例修改了这个账号的状态比如改了密码、锁定了账号后面的用例就会失败看是否有静态变量或全局变量被修改看是否存在数据库脏数据残留。解决核心思路是“用例隔离”。每个用例有自己的测试数据执行前清理、执行后销毁。接口测试中可以用事务回滚UI测试中可以每次创建一个新用户来测试。如果实在无法隔离至少要把用例执行顺序固定下来并在报告里注明依赖关系。还有个小技巧排查用例间影响时可以分批次禁用用例执行来二分定位。先把后半段用例全注释掉跑一次再把前半段注释掉跑一次对比哪几条用例在一起才出问题很快就能锁定。6.6 测试进程卡死不退出现象测试全部跑完了但命令迟迟不结束一直挂着。原因常见于代码里开启了线程、连接池、浏览器驱动等资源没有关闭。比如Selenium的webdriver.Chrome()如果没调用quit()进程就不会退出。解决在tearDown里显式关闭资源def tearDown(self): if hasattr(self, driver): self.driver.quit()或者使用addCleanup注册清理函数即使断言失败也会执行如果用了线程池在tearDownClass里调用executor.shutdown(waitTrue)。排查时可以用“二分注释法”——注释掉部分用例看是否还会卡死缩小范围后再定位是哪个资源没释放。处理这类问题虽然费时但一旦解决了整个测试链路的稳定性会提升一大截。7. 一个人人可复用的最小自动化测试骨架理论说了一堆最后给你一套可以直接拿去用的最小落地骨架。我把它写成了可以被整体拷贝的项目结构注释写得比较详细方便你对照修改。auto_test_demo/ ├── common/ │ ├── __init__.py │ ├── config.py # 全局配置 │ └── http_client.py # HTTP请求封装 ├── tests/ │ ├── __init__.py │ ├── test_login.py │ └── test_user.py ├── reports/ # 报告输出目录 ├── run_all.py # 统一入口 └── requirements.txtcommon/config.pyimport os BASE_URL https://api.example.com TIMEOUT 10 REPORT_DIR os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), reports)common/http_client.pyimport requests class HttpClient: def __init__(self, base_url): self.base_url base_url self.session requests.Session() def get(self, url, **kwargs): return self.session.get(self.base_url url, **kwargs) def post(self, url, **kwargs): return self.session.post(self.base_url url, **kwargs) def close(self): self.session.close()run_all.pyimport unittest from datetime import datetime import os from HTMLTestRunner import HTMLTestRunner from common import config if __name__ __main__: suite unittest.TestLoader().discover(./tests, patterntest_*.py) if not os.path.exists(config.REPORT_DIR): os.makedirs(config.REPORT_DIR) now datetime.now().strftime(%Y%m%d_%H%M%S) report_file os.path.join(config.REPORT_DIR, freport_{now}.html) with open(report_file, wb) as f: runner HTMLTestRunner(streamf, title自动化测试报告, description执行时间 now) runner.run(suite)把这套骨架跑起来之后你再根据项目实际情况去填充和扩展会比从零开始舒服很多。说到最后我个人的一个明显体会是Unittest这套框架本身并不难难的是你想清楚为什么用、怎么组织好测试、怎么持续地维护下去。很多团队买了不少工具、写了不少脚本最后自动化测试还是形同虚设根子不在技术上而在流程和认知上。把测试当作一个工程来做而不是一堆临时脚本的组合这一条想明白了自动化测试才真正开始发挥它的价值。至于用Unittest还是pytest是次要问题思路通了换任何一个框架都是两天的事。
返回列表