郑州做网站推广多少钱房产网站编辑如何做

张小明 2026/1/12 22:00:34
郑州做网站推广多少钱,房产网站编辑如何做,微博网页版入口,哪个网站开发小程序一、引言#xff1a;为什么测试是优秀程序员的第二本能#xff1f; 在当今快节奏的软件开发环境中#xff0c;测试已不再是可选的附加项#xff0c;而是高质量代码的基石。对于Python开发者而言#xff0c;掌握测试技能不仅是提升代码质量的利器#xff0c;更…一、引言为什么测试是优秀程序员的第二本能在当今快节奏的软件开发环境中测试已不再是可选的附加项而是高质量代码的基石。对于Python开发者而言掌握测试技能不仅是提升代码质量的利器更是面试中区分初级与中高级开发者的重要标志。测试在面试中的关键价值工程素养的体现展示你对代码质量的关注程度团队协作能力证明你能编写可维护、可测试的代码问题预防意识体现你具备前瞻性思维能防患于未然自动化思维展示你对开发效率的追求真实案例2018年某知名电商公司因测试覆盖不足导致促销活动计算错误造成数千万元损失。事后分析发现缺失的价格计算单元测试是主要原因之一。这个案例深刻说明了测试在商业系统中的重要性。本章将深入探讨Python两大主流测试框架unittest和pytest从基础概念到高级实战从单元测试到集成测试带你构建完整的测试知识体系。二、单元测试基础从理论到实践2.1 单元测试的核心概念什么是单元测试单元测试是针对软件中最小的可测试单元通常是函数或方法进行验证的测试方法。它的核心目标是隔离测试确保每个单元在独立环境中按预期工作。单元测试的FIRST原则Fast快速测试应该快速执行Independent独立测试之间不应该相互依赖Repeatable可重复在任何环境中都能重复执行Self-Validating自验证测试能明确显示通过或失败Timely及时在编写生产代码前或同时编写测试2.2 unittest框架深度解析2.2.1 unittest基础架构# test_calculator_unittest.pyimportunittestimportsysimportos# 添加项目根目录到Python路径sys.path.insert(0,os.path.abspath(os.path.join(os.path.dirname(__file__),..)))# 被测试的模块classCalculator:一个简单的计算器类用于演示单元测试def__init__(self):self._memory0defadd(self,a:float,b:float)-float:返回两个数的和ifnot(isinstance(a,(int,float))andisinstance(b,(int,float))):raiseTypeError(参数必须是数字)returnabdefsubtract(self,a:float,b:float)-float:返回两个数的差returna-bdefmultiply(self,a:float,b:float)-float:返回两个数的积returna*bdefdivide(self,a:float,b:float)-float:返回两个数的商ifb0:raiseZeroDivisionError(除数不能为零)returna/bdefpower(self,base:float,exponent:float)-float:计算幂次returnbase**exponentdefsqrt(self,x:float)-float:计算平方根ifx0:raiseValueError(负数没有实数平方根)returnx**0.5defmemory_store(self,value:float)-None:存储到内存self._memoryvaluedefmemory_recall(self)-float:从内存读取returnself._memorydefmemory_clear(self)-None:清除内存self._memory0# 测试类classTestCalculator(unittest.TestCase): unittest测试类的标准结构 必须继承unittest.TestCase classmethoddefsetUpClass(cls):类级别设置 - 所有测试开始前执行一次print(\n*60)print(开始执行Calculator测试套件)print(*60)classmethoddeftearDownClass(cls):类级别清理 - 所有测试结束后执行一次print(\n*60)print(Calculator测试套件执行完成)print(*60)defsetUp(self):方法级别设置 - 每个测试方法前执行self.calcCalculator()self.test_idself._testMethodName# 获取当前测试方法名print(f\n开始测试:{self.test_id})deftearDown(self):方法级别清理 - 每个测试方法后执行print(f完成测试:{self.test_id})# 测试基本算术运算 deftest_add_positive_numbers(self):测试正数相加resultself.calc.add(3,4)self.assertEqual(result,7,3 4 应该等于 7)deftest_add_negative_numbers(self):测试负数相加resultself.calc.add(-3,-4)self.assertEqual(result,-7,-3 (-4) 应该等于 -7)deftest_add_mixed_numbers(self):测试正负数混合相加resultself.calc.add(5,-3)self.assertEqual(result,2,5 (-3) 应该等于 2)deftest_add_float_numbers(self):测试浮点数相加resultself.calc.add(2.5,3.1)self.assertAlmostEqual(result,5.6,places7,msg2.5 3.1 应该约等于 5.6)deftest_add_type_error(self):测试类型错误withself.assertRaises(TypeError)ascontext:self.calc.add(2,3)self.assertIn(参数必须是数字,str(context.exception))# 测试除法运算 deftest_divide_normal(self):测试正常除法resultself.calc.divide(10,2)self.assertEqual(result,5,10 / 2 应该等于 5)deftest_divide_by_zero(self):测试除零异常withself.assertRaises(ZeroDivisionError)ascontext:self.calc.divide(10,0)self.assertEqual(str(context.exception),除数不能为零)deftest_divide_float_result(self):测试除法结果为浮点数resultself.calc.divide(5,2)self.assertEqual(result,2.5,5 / 2 应该等于 2.5)# 测试幂运算 deftest_power_positive_exponent(self):测试正指数resultself.calc.power(2,3)self.assertEqual(result,8,2^3 应该等于 8)deftest_power_zero_exponent(self):测试零指数resultself.calc.power(5,0)self.assertEqual(result,1,任何数的0次方应该等于1)deftest_power_negative_exponent(self):测试负指数resultself.calc.power(2,-2)self.assertEqual(result,0.25,2^(-2) 应该等于 0.25)# 测试平方根运算 deftest_sqrt_positive(self):测试正数平方根resultself.calc.sqrt(9)self.assertEqual(result,3,√9 应该等于 3)deftest_sqrt_zero(self):测试零的平方根resultself.calc.sqrt(0)self.assertEqual(result,0,√0 应该等于 0)deftest_sqrt_negative(self):测试负数平方根异常withself.assertRaises(ValueError)ascontext:self.calc.sqrt(-4)self.assertIn(负数没有实数平方根,str(context.exception))# 测试内存功能 deftest_memory_operations(self):测试内存存储、读取和清除# 存储self.calc.memory_store(42)self.assertEqual(self.calc.memory_recall(),42,内存值应该是42)# 更新self.calc.memory_store(100)self.assertEqual(self.calc.memory_recall(),100,内存值应该是100)# 清除self.calc.memory_clear()self.assertEqual(self.calc.memory_recall(),0,清除后内存值应该是0)# 测试边缘情况 deftest_large_numbers(self):测试大数运算resultself.calc.add(1e100,1e100)self.assertEqual(result,2e100,大数加法应该正确)deftest_small_numbers(self):测试小数运算resultself.calc.multiply(0.1,0.2)self.assertAlmostEqual(result,0.02,places10,msg小数乘法应该正确)# 跳过测试示例 unittest.skip(暂时跳过这个测试等待功能完善)deftest_skip_example(self):这个测试会被跳过self.fail(这个测试不应该被执行)unittest.skipIf(sys.version_info(3,8),需要Python 3.8或更高版本)deftest_python_version_dependent(self):Python版本依赖的测试# 这里可以使用Python 3.8的特性self.assertTrue(sys.version_info(3,8))# 预期失败测试示例 unittest.expectedFailuredeftest_expected_failure(self):这个测试预期会失败# 这里有一个已知的bug我们标记为预期失败resultself.calc.divide(1,3)*3self.assertEqual(result,1)# 由于浮点数精度问题可能失败# 参数化测试的替代方案 deftest_add_multiple_cases(self):通过循环模拟参数化测试test_cases[(1,2,3),(0,0,0),(-1,1,0),(2.5,3.5,6.0),]fora,b,expectedintest_cases:withself.subTest(aa,bb,expectedexpected):resultself.calc.add(a,b)self.assertEqual(result,expected,f{a}{b}应该等于{expected})deftest_divide_multiple_cases(self):除法多场景测试test_cases[(10,2,5),(0,5,0),(-10,2,-5),(1,3,1/3),]fora,b,expectedintest_cases:withself.subTest(aa,bb,expectedexpected):resultself.calc.divide(a,b)self.assertAlmostEqual(result,expected,places7,msgf{a}/{b}应该约等于{expected})if__name____main__:# 创建测试加载器loaderunittest.TestLoader()# 创建测试套件suiteunittest.TestSuite()# 添加测试类suite.addTests(loader.loadTestsFromTestCase(TestCalculator))# 创建测试运行器runnerunittest.TextTestRunner(verbosity2)# 运行测试resultrunner.run(suite)# 输出统计信息print(f\n测试结果统计:)print(f运行测试数:{result.testsRun})print(f失败数:{len(result.failures)})print(f错误数:{len(result.errors)})print(f跳过数:{len(result.skipped)})# 如果有失败或错误输出详细信息ifresult.failures:print(\n失败的测试:)fortest,tracebackinresult.failures:print(f{test}:)print(f{traceback.splitlines()[-1]})ifresult.errors:print(\n错误的测试:)fortest,tracebackinresult.errors:print(f{test}:)print(f{traceback.splitlines()[-1]})# 退出码sys.exit(0ifresult.wasSuccessful()else1)2.2.2 unittest断言方法大全# test_assertions.pyimportunittestclassTestAssertions(unittest.TestCase):展示unittest所有断言方法deftest_basic_assertions(self):基本断言# 相等/不等self.assertEqual(34,7)# 检查相等self.assertNotEqual(34,8)# 检查不等# 布尔检查self.assertTrue(12)# 检查为Trueself.assertFalse(12)# 检查为False# None检查self.assertIsNone(None)# 检查为Noneself.assertIsNotNone([])# 检查不为Nonedeftest_identity_assertions(self):身份断言is/is not检查a[1,2,3]ba c[1,2,3]self.assertIs(a,b)# 检查是同一个对象self.assertIsNot(a,c)# 检查不是同一个对象deftest_container_assertions(self):容器断言# 包含检查self.assertIn(3,[1,2,3])# 检查元素在容器中self.assertNotIn(4,[1,2,3])# 检查元素不在容器中# 序列比较self.assertListEqual([1,2,3],[1,2,3])self.assertTupleEqual((1,2),(1,2))self.assertSetEqual({1,2},{2,1})self.assertDictEqual({a:1},{a:1})# 序列顺序无关比较self.assertCountEqual([1,2,3],[3,1,2])deftest_exception_assertions(self):异常断言# 检查是否抛出特定异常withself.assertRaises(ValueError):int(abc)# 检查异常消息withself.assertRaises(ValueError)ascm:int(abc)self.assertIn(invalid literal,str(cm.exception))# 检查警告importwarningswithself.assertWarns(DeprecationWarning):warnings.warn(This is deprecated,DeprecationWarning)deftest_numeric_assertions(self):数值断言# 比较self.assertGreater(5,3)# 检查大于self.assertGreaterEqual(5,5)# 检查大于等于self.assertLess(3,5)# 检查小于self.assertLessEqual(3,3)# 检查小于等于# 近似相等浮点数self.assertAlmostEqual(0.10.2,0.3,places7)self.assertNotAlmostEqual(0.10.2,0.4,places7)deftest_regex_assertions(self):正则断言self.assertRegex(hello world,rhello)# 检查匹配正则self.assertNotRegex(hello world,rgoodbye)# 检查不匹配正则deftest_filesystem_assertions(self):文件系统断言importtempfileimportos# 创建临时文件withtempfile.NamedTemporaryFile(modew,deleteFalse)asf:f.write(test content)temp_pathf.nametry:# 检查文件存在self.assertTrue(os.path.exists(temp_path))# 检查文件相等需要先读取内容withopen(temp_path,r)asf:contentf.read()self.assertEqual(content,test content)finally:# 清理os.unlink(temp_path)2.2.3 unittest.mock深度应用# test_mocking.pyimportunittestfromunittest.mockimportMock,MagicMock,patch,call,PropertyMockimportdatetimeimportrequestsclassTestMocking(unittest.TestCase):展示unittest.mock的各种用法deftest_basic_mock(self):基础Mock使用# 创建Mock对象mock_objMock()# 调用Mock对象mock_obj.some_method()# 验证方法被调用mock_obj.some_method.assert_called_once()deftest_mock_with_return_value(self):设置Mock返回值mock_objMock()# 设置方法返回值mock_obj.get_value.return_value42# 调用并验证resultmock_obj.get_value()self.assertEqual(result,42)# 可以多次调用返回相同值self.assertEqual(mock_obj.get_value(),42)self.assertEqual(mock_obj.get_value(),42)# 验证调用次数self.assertEqual(mock_obj.get_value.call_count,3)deftest_mock_with_side_effect(self):使用side_effect控制Mock行为mock_objMock()# 1. 返回序列值mock_obj.get_next.side_effect[1,2,3]self.assertEqual(mock_obj.get_next(),1)self.assertEqual(mock_obj.get_next(),2)self.assertEqual(mock_obj.get_next(),3)# 2. 抛出异常mock_obj.raise_error.side_effectValueError(test error)withself.assertRaises(ValueError):mock_obj.raise_error()# 3. 使用函数defdynamic_response(x):returnx*2mock_obj.double.side_effectdynamic_response self.assertEqual(mock_obj.double(5),10)self.assertEqual(mock_obj.double(10),20)deftest_mock_verification(self):验证Mock调用mock_objMock()# 调用方法mock_obj.process(item1,item2)mock_obj.process(item3,item4)# 验证调用mock_obj.process.assert_called()# 至少调用一次mock_obj.process.assert_called_with(item3,item4)# 最后一次调用参数# 验证特定调用expected_calls[call(item1,item2),call(item3,item4)]mock_obj.process.assert_has_calls(expected_calls)# 验证调用顺序mock_obj.process.assert_has_calls(expected_calls,any_orderFalse)deftest_mock_attributes(self):Mock对象的属性mock_objMock()# 设置属性mock_obj.nameTest Objectmock_obj.value100self.assertEqual(mock_obj.name,Test Object)self.assertEqual(mock_obj.value,100)# 动态属性MagicMock特性magic_objMagicMock()magic_obj.dynamic_attribute42self.assertEqual(magic_obj.dynamic_attribute,42)deftest_patch_decorator(self):使用patch装饰器# 被测试的函数defget_current_year():returndatetime.datetime.now().year# 测试时替换datetime.datetime.nowpatch(datetime.datetime)deftest_get_current_year(mock_datetime,self):# 配置mockmock_nowMock()mock_now.year2023mock_datetime.now.return_valuemock_now# 测试resultget_current_year()self.assertEqual(result,2023)# 验证mock被调用mock_datetime.now.assert_called_once()# 运行测试test_get_current_year(self)deftest_patch_context_manager(self):使用patch上下文管理器defget_website_title(url):responserequests.get(url)returnresponse.text.split(title)[1].split(/title)[0]# 使用上下文管理器替换requests.getwithpatch(requests.get)asmock_get:# 配置mock响应mock_responseMock()mock_response.texthtmltitleTest Page/title/htmlmock_get.return_valuemock_response# 测试titleget_website_title(http://example.com)self.assertEqual(title,Test Page)# 验证mock_get.assert_called_once_with(http://example.com)deftest_patch_object(self):替换对象的特定方法classDataProcessor:def__init__(self):self.data[]deffetch_data(self):# 假设这是从外部获取数据return[1,2,3]defprocess(self):dataself.fetch_data()returnsum(data)processorDataProcessor()# 替换fetch_data方法withpatch.object(processor,fetch_data,return_value[10,20,30]):resultprocessor.process()self.assertEqual(result,60)deftest_property_mock(self):Mock属性classTemperatureSensor:propertydeftemperature(self):# 假设这是从硬件读取温度return25.0sensorTemperatureSensor()# Mock temperature属性withpatch.object(TemperatureSensor,temperature,new_callablePropertyMock)asmock_temp:mock_temp.return_value30.5# 现在temperature属性返回30.5self.assertEqual(sensor.temperature,30.5)# 可以多次访问self.assertEqual(sensor.temperature,30.5)# 验证访问次数self.assertEqual(mock_temp.call_count,2)三、pytest现代Python测试框架3.1 pytest核心优势与基础3.1.1 pytest vs unittest对比# test_comparison.py pytest与unittest的核心差异对比 # unittest风格 importunittestclassTestUnittestStyle(unittest.TestCase):defsetUp(self):self.data[1,2,3]deftest_sum(self):self.assertEqual(sum(self.data),6)deftest_length(self):self.assertEqual(len(self.data),3)# pytest风格 classTestPytestStyle:pytest测试类不需要继承特定基类defsetup_method(self):pytest的setup方法self.data[1,2,3]deftest_sum(self):使用原生assert语句assertsum(self.data)6deftest_length(self):assertlen(self.data)3deftest_with_fixture(self,sample_fixture):使用fixtureassertsample_fixturefixture data# pytest还可以直接使用函数deftest_function_style():函数式测试data[1,2,3]assertsum(data)6assertlen(data)33.1.2 pytest安装与配置# 安装pytestpipinstallpytest# 安装常用插件pipinstallpytest-cov# 测试覆盖率pipinstallpytest-xdist# 并行测试pipinstallpytest-mock# Mock支持pipinstallpytest-html# HTML报告pipinstallpytest-asyncio# 异步测试pipinstallpytest-bdd# 行为驱动开发pipinstallpytest-timeout# 超时控制pipinstallpytest-randomly# 随机顺序运行# 运行测试pytest# 运行所有测试pytest tests/# 运行特定目录pytest test_file.py# 运行特定文件pytest -ktest_add# 运行名称包含test_add的测试pytest -x# 遇到第一个失败就停止pytest -v# 详细输出pytest --tbshort# 简短回溯信息pytest --lf# 只运行上次失败的测试# 生成报告pytest --htmlreport.html# HTML报告pytest --covmyapp# 覆盖率报告pytest --covmyapp --cov-reporthtml# HTML覆盖率报告3.1.3 pytest配置文件# pytest.ini [pytest] # 测试文件匹配模式 testpaths tests python_files test_*.py python_classes Test* python_functions test_* # 命令行选项默认值 addopts -v --tbshort --strict-markers --coloryes # 日志配置 log_cli true log_cli_level INFO log_cli_format %(asctime)s [%(levelname)s] %(message)s log_cli_date_format %Y-%m-%d %H:%M:%S # 自定义标记 markers slow: marks tests as slow (deselect with -m not slow) integration: integration tests smoke: smoke tests unit: unit tests db: tests that require database network: tests that require network windows: tests specific to Windows linux: tests specific to Linux # 测试超时秒 timeout 300 # 并行测试 # addopts -n auto # 覆盖率配置 # addopts --covmyapp --cov-reportterm-missing --cov-reporthtml3.2 pytest高级特性3.2.1 Fixture系统深度解析# test_fixtures.pyimportpytestimporttempfileimportosimportsqlite3importtime# 基础fixturepytest.fixturedefsample_data():返回测试数据return[1,2,3,4,5]deftest_sum(sample_data):使用fixtureassertsum(sample_data)15# fixture作用域pytest.fixture(scopefunction)deffunc_fixture():函数作用域 - 每个测试函数执行一次print(function fixture setup)yieldfunction dataprint(function fixture teardown)pytest.fixture(scopeclass)defclass_fixture():类作用域 - 每个测试类执行一次print(class fixture setup)yieldclass dataprint(class fixture teardown)pytest.fixture(scopemodule)defmodule_fixture():模块作用域 - 每个模块执行一次print(module fixture setup)yieldmodule dataprint(module fixture teardown)pytest.fixture(scopesession)defsession_fixture():会话作用域 - 整个测试会话执行一次print(session fixture setup)yieldsession dataprint(session fixture teardown)# 自动使用fixturepytest.fixture(autouseTrue)defauto_fixture():自动应用到所有测试start_timetime.time()yieldend_timetime.time()print(f\n测试执行时间:{end_time-start_time:.3f}秒)# 带清理的fixturepytest.fixturedeftemporary_file():创建临时文件测试后自动清理withtempfile.NamedTemporaryFile(modew,deleteFalse,suffix.txt)asf:f.write(test content)file_pathf.nameyieldfile_path# 提供文件路径给测试# 测试完成后清理ifos.path.exists(file_path):os.unlink(file_path)deftest_temporary_file(temporary_file):测试临时文件assertos.path.exists(temporary_file)withopen(temporary_file,r)asf:contentf.read()assertcontenttest content# 数据库fixture示例pytest.fixturedefdatabase():内存数据库fixtureconnsqlite3.connect(:memory:)cursorconn.cursor()# 创建表cursor.execute( CREATE TABLE users ( id INTEGER PRIMARY KEY, username TEXT NOT NULL, email TEXT NOT NULL UNIQUE ) )# 插入测试数据cursor.execute(INSERT INTO users (username, email) VALUES (alice, aliceexample.com))cursor.execute(INSERT INTO users (username, email) VALUES (bob, bobexample.com))conn.commit()yieldconn# 提供数据库连接# 清理conn.close()deftest_database_query(database):测试数据库查询cursordatabase.cursor()cursor.execute(SELECT COUNT(*) FROM users)countcursor.fetchone()[0]assertcount2# fixture依赖其他fixturepytest.fixturedefuser_data():用户数据return{username:testuser,email:testexample.com}pytest.fixturedefcreate_user(database,user_data):创建用户依赖database和user_data fixturecursordatabase.cursor()cursor.execute(INSERT INTO users (username, email) VALUES (?, ?),(user_data[username],user_data[email]))database.commit()user_idcursor.lastrowidreturn{id:user_id,**user_data}deftest_create_user(create_user):测试用户创建assertcreate_user[id]isnotNoneassertcreate_user[username]testuser# 参数化fixturepytest.fixture(params[json,xml,yaml])defdata_format(request):参数化fixture - 测试不同数据格式returnrequest.paramdeftest_data_processing(data_format):测试不同数据格式的处理# 这里可以根据data_format进行不同的测试assertdata_formatin[json,xml,yaml]# 工厂模式fixturepytest.fixturedefmake_user():用户工厂def_make_user(usernameuser,emailNone):ifemailisNone:emailf{username}example.comreturn{username:username,email:email}return_make_userdeftest_user_factory(make_user):测试用户工厂user1make_user(alice)user2make_user(bob,bobcompany.com)assertuser1[username]aliceassertuser1[email]aliceexample.comassertuser2[email]bobcompany.com# 动态fixturepytest.fixturedefdynamic_data(request):根据测试需求动态生成数据markerrequest.node.get_closest_marker(data_size)ifmarker:sizemarker.args[0]ifmarker.argselse10else:size5returnlist(range(size))pytest.mark.data_size(20)deftest_large_dataset(dynamic_data):测试大数据集assertlen(dynamic_data)20deftest_small_dataset(dynamic_data):测试小数据集assertlen(dynamic_data)53.2.2 参数化测试# test_parametrize.pyimportpytestimportmath# 基本参数化pytest.mark.parametrize(a,b,expected,[(1,2,3),(5,-1,4),(0,0,0),(2.5,3.1,5.6),])deftest_add_parametrize(a,b,expected):参数化测试加法assertabexpected# 参数化类pytest.mark.parametrize(input_str,expected,[(35,8),(2*4,8),(10/2,5),])classTestEval:参数化测试类deftest_eval(self,input_str,expected):测试eval函数asserteval(input_str)expecteddeftest_eval_length(self,input_str):测试输入长度assertlen(input_str)0# 多个参数化组合pytest.mark.parametrize(x,[0,1,2])pytest.mark.parametrize(y,[10,20])deftest_combinations(x,y):测试参数组合resultxyassertresult10assertresult22# 参数化id自定义pytest.mark.parametrize(input,expected,[pytest.param(3,9,idsquare_of_3),pytest.param(4,16,idsquare_of_4),pytest.param(5,25,idsquare_of_5),pytest.param(0,0,idsquare_of_0),])deftest_square(input,expected):测试平方计算assertinput**2expected# 参数化fixture组合pytest.fixture(params[lower,UPPER,Mixed])defstring_case(request):参数化fixturereturnrequest.paramdeftest_string_operations(string_case):测试字符串操作test_stringhelloifstring_caselower:asserttest_string.lower()helloelifstring_caseUPPER:asserttest_string.upper()HELLOelifstring_caseMixed:asserttest_string.title()Hello# 复杂参数化测试边界值pytest.mark.parametrize(value,expected,[# 边界值测试(0,zero),(1,positive),(-1,negative),(999999,positive),# 大正数(-999999,negative),# 大负数# 特殊值测试(float(inf),infinity),(float(-inf),negative infinity),(float(nan),nan),# 注意nan ! nan])deftest_number_classification(value,expected):数字分类测试defclassify_number(n):ifmath.isinf(n):returninfinityifn0elsenegative infinityelifmath.isnan(n):returnnanelifn0:returnzeroelifn0:returnpositiveelse:returnnegativeresultclassify_number(value)# 特殊处理nanifexpectednan:assertmath.isnan(value)assertmath.isnan(float(result))ifresultnanelseFalseelse:assertresultexpected3.2.3 标记Markers系统# test_markers.pyimportpytestimporttimeimportsysimportplatform# 自定义标记pytest.mark.slowdeftest_slow_operation():慢速测试time.sleep(2)assertTruepytest.mark.integrationdeftest_integration():集成测试assertTruepytest.mark.apideftest_api_endpoint():API测试assertTruepytest.mark.databasedeftest_database_query():数据库测试assertTrue# 组合标记pytest.mark.slowpytest.mark.integrationdeftest_slow_integration():慢速集成测试time.sleep(1)assertTrue# 跳过测试pytest.mark.skip(reason功能尚未实现)deftest_unimplemented():未实现的功能测试assertFalsepytest.mark.skipif(sys.version_info(3,8),reason需要Python 3.8)deftest_python38_feature():Python 3.8特性测试# 使用walrus运算符if(data:[1,2,3]):assertlen(data)3pytest.mark.skipif(platform.system()!Linux,reason仅支持Linux)deftest_linux_specific():Linux特定功能测试assertplatform.system()Linux# 预期失败pytest.mark.xfail(reason已知问题正在修复)deftest_known_issue():已知问题的测试assertFalse# 预期失败pytest.mark.xfail(strictTrue)# 如果通过则报错deftest_should_fail():应该失败的测试assertFalse# 参数化标记组合pytest.mark.parametrize(x,[1,2,3])pytest.mark.quickdeftest_quick_parametrized(x):快速参数化测试assertx0# 自定义标记使用pytest.mark.securitydeftest_security_feature():安全功能测试assertTruepytest.mark.performancedeftest_performance():性能测试importrandom data[random.random()for_inrange(10000)]sorted_datasorted(data)assertlen(sorted_data)10000# 标记类pytest.mark.allclassTestAllFeatures:包含所有功能的测试类pytest.mark.feature_adeftest_feature_a(self):assertTruepytest.mark.feature_bdeftest_feature_b(self):assertTruepytest.mark.feature_cpytest.mark.slowdeftest_feature_c(self):time.sleep(0.5)assertTrue# 运行特定标记的测试 pytest -m slow # 只运行slow标记的测试 pytest -m not slow # 运行除slow外的所有测试 pytest -m integration and not slow # 运行集成测试中非慢速的 pytest -m security or performance # 运行安全或性能测试 3.2.4 conftest.py共享fixture# conftest.py conftest.py - pytest的共享配置文件 在同一目录或父目录中的conftest.py会被自动加载 importpytestimporttempfileimportosimportjsonimportyamlfromdatetimeimportdatetime# 会话级别的fixturepytest.fixture(scopesession)defsession_data():会话级数据整个测试会话只创建一次return{session_start:datetime.now()}# 应用配置fixturepytest.fixture(scopesession)defapp_config():应用配置return{database:{host:localhost,port:5432,name:test_db},api:{timeout:30,max_retries:3}}# 临时目录fixturepytest.fixturedeftemp_dir():创建临时目录withtempfile.TemporaryDirectory()astmpdir:yieldtmpdir# JSON数据fixturepytest.fixturedefsample_json_data():示例JSON数据return{users:[{id:1,name:Alice,role:admin},{id:2,name:Bob,role:user}],settings:{theme:dark,notifications:True}}pytest.fixturedefjson_file(temp_dir,sample_json_data):创建JSON文件file_pathos.path.join(temp_dir,data.json)withopen(file_path,w)asf:json.dump(sample_json_data,f)returnfile_path# YAML数据fixturepytest.fixturedefsample_yaml_data():示例YAML数据return database: host: localhost port: 3306 name: test server: port: 8080 debug: true pytest.fixturedefyaml_file(temp_dir,sample_yaml_data):创建YAML文件file_pathos.path.join(temp_dir,config.yaml)withopen(file_path,w)asf:f.write(sample_yaml_data)returnfile_path# 测试数据工厂pytest.fixturedefuser_factory():用户数据工厂defcreate_user(**kwargs):user{id:kwargs.get(id,1),username:kwargs.get(username,testuser),email:kwargs.get(email,testexample.com),active:kwargs.get(active,True),created_at:kwargs.get(created_at,datetime.now())}returnuserreturncreate_user# 请求模拟fixturepytest.fixturedefmock_response():模拟HTTP响应classMockResponse:def__init__(self,status_code200,json_dataNone,text):self.status_codestatus_code self._json_datajson_data self.texttextdefjson(self):returnself._json_datadefraise_for_status(self):if400self.status_code600:raiseException(fHTTP Error:{self.status_code})returnMockResponse# 数据库连接池fixturepytest.fixture(scopesession)defdb_pool():数据库连接池模拟classDBPool:def__init__(self):self.connections[]self.max_size10defget_connection(self):iflen(self.connections)self.max_size:connMockConnection()self.connections.append(conn)returnconnelse:raiseException(Connection pool exhausted)defclose_all(self):self.connections.clear()classMockConnection:defexecute(self,query):returnMockResult()defclose(self):passclassMockResult:deffetchall(self):return[]poolDBPool()yieldpool pool.close_all()# 自定义标记注册defpytest_configure(config):pytest配置钩子config.addinivalue_line(markers,smoke: 冒烟测试 - 核心功能验证)config.addinivalue_line(markers,regression: 回归测试 - 确保已有功能正常)config.addinivalue_line(markers,e2e: 端到端测试 - 完整用户流程测试)# 测试结果收集钩子defpytest_terminal_summary(terminalreporter,exitstatus,config):终端总结钩子ifterminalreporter.stats.get(passed):terminalreporter.write_sep(,测试通过情况)terminalreporter.write_line(f通过测试数:{len(terminalreporter.stats[passed])})ifterminalreporter.stats.get(failed):terminalreporter.write_sep(,测试失败情况)terminalreporter.write_line(f失败测试数:{len(terminalreporter.stats[failed])})# 计算测试执行时间durationtime.time()-terminalreporter._sessionstarttime terminalreporter.write_line(f\n总执行时间:{duration:.2f}秒)3.3 pytest插件生态3.3.1 常用插件示例# test_plugins.pyimportpytestimportasyncioimporttime# pytest-asyncio异步测试pytest.mark.asyncioasyncdeftest_async_function():异步测试示例awaitasyncio.sleep(0.1)assertTrue# pytest-mock更简洁的mockdeftest_with_pytest_mock(mocker):使用pytest-mockmock_requestsmocker.patch(requests.get)mock_responsemocker.Mock()mock_response.json.return_value{status:ok}mock_requests.return_valuemock_response# 调用被测试代码importrequests responserequests.get(http://example.com)dataresponse.json()assertdata[status]okmock_requests.assert_called_once_with(http://example.com)# pytest-timeout超时控制pytest.mark.timeout(5)# 5秒超时deftest_timeout():超时测试time.sleep(10)# 这会触发超时# pytest-cov覆盖率测试 运行命令 pytest --covmyapp tests/ pytest --covmyapp --cov-reporthtml --cov-reportterm # pytest-xdist并行测试 运行命令 pytest -n auto # 自动检测CPU核心数 pytest -n 4 # 使用4个worker pytest -n 2 --distloadscope # 按作用域分配 # pytest-htmlHTML报告 运行命令 pytest --htmlreport.html --self-contained-html # pytest-randomly随机顺序运行 运行命令 pytest --randomly-seed42 # 固定随机种子 # pytest-bdd行为驱动开发 # features/test_feature.feature Feature: 计算器功能 Scenario: 两数相加 Given 我有一个计算器 When 我输入 23 Then 结果应该是 5 # pytest-djangoDjango测试 # conftest.py pytest_plugins [pytest_django] # settings_test.py SECRET_KEY test-secret-key DATABASES { default: { ENGINE: django.db.backends.sqlite3, NAME: :memory:, } } 四、测试策略与最佳实践4.1 测试金字塔与测试策略各层测试的特点与职责4.1.1 单元测试层# tests/unit/test_services.py 单元测试特点 - 快速执行毫秒级 - 完全隔离使用mock - 高覆盖率80% - 验证单个函数/方法 importpytestfromunittest.mockimportMock,patchfromapp.servicesimportUserService,PaymentService,EmailServiceclassTestUserService:用户服务单元测试pytest.fixturedefmock_db(self):模拟数据库returnMock()pytest.fixturedefuser_service(self,mock_db):创建用户服务实例returnUserService(dbmock_db)deftest_create_user(self,user_service,mock_db):测试创建用户# Arrangeuser_data{username:alice,email:aliceexample.com}mock_db.execute.return_value1# 模拟插入成功# Actresultuser_service.create_user(user_data)# Assertassertresult[id]1assertresult[username]alicemock_db.execute.assert_called_once()deftest_create_user_duplicate_email(self,user_service,mock_db):测试重复邮箱mock_db.execute.side_effectException(Duplicate email)withpytest.raises(ValueError,match邮箱已存在):user_service.create_user({email:existingexample.com})classTestPaymentService:支付服务单元测试deftest_process_payment_success(self,mocker):测试支付成功# Mock外部依赖mock_gatewaymocker.Mock()mock_gateway.charge.return_value{status:success,transaction_id:txn_123}servicePaymentService(gatewaymock_gateway)resultservice.process_payment(amount100,tokentok_123)assertresult[success]isTrueasserttransaction_idinresult mock_gateway.charge.assert_called_once_with(100,tok_123)deftest_process_payment_insufficient_funds(self,mocker):测试余额不足mock_gatewaymocker.Mock()mock_gateway.charge.side_effectException(Insufficient funds)servicePaymentService(gatewaymock_gateway)resultservice.process_payment(amount1000,tokentok_123)assertresult[success]isFalseassert余额不足inresult[error]4.1.2 集成测试层# tests/integration/test_api_integration.py 集成测试特点 - 中等速度秒级 - 部分真实依赖数据库、缓存 - 验证组件间协作 - 关注接口契约 importpytestimportrequestsfromappimportcreate_appfromapp.databaseimportdbfromapp.modelsimportUser,Product,Orderpytest.fixture(scopemodule)defapp():创建测试应用appcreate_app(testing)withapp.app_context():db.create_all()yieldapp db.drop_all()pytest.fixturedefclient(app):测试客户端returnapp.test_client()pytest.fixturedefauth_header():认证头return{Authorization:Bearer test-token}classTestUserAPI:用户API集成测试deftest_create_user_success(self,client):测试创建用户成功responseclient.post(/api/users,json{username:testuser,email:testexample.com,password:secure123})assertresponse.status_code201dataresponse.get_json()assertdata[username]testuserassertidindatadeftest_create_user_duplicate(self,client):测试重复用户# 第一次创建client.post(/api/users,json{username:duplicate,email:duplicateexample.com,password:secure123})# 第二次创建应该失败responseclient.post(/api/users,json{username:duplicate,email:duplicateexample.com,password:secure123})assertresponse.status_code400deftest_login_success(self,client):测试登录成功# 先创建用户client.post(/api/users,json{username:loginuser,email:loginexample.com,password:secure123})# 登录responseclient.post(/api/login,json{email:loginexample.com,password:secure123})assertresponse.status_code200dataresponse.get_json()asserttokenindatadeftest_login_failure(self,client):测试登录失败responseclient.post(/api/login,json{email:nonexistentexample.com,password:wrongpass})assertresponse.status_code401classTestOrderAPI:订单API集成测试pytest.fixturedefsample_user(self,app):创建测试用户withapp.app_context():userUser(usernameorderuser,emailorderexample.com)db.session.add(user)db.session.commit()returnuserpytest.fixturedefsample_product(self,app):创建测试商品withapp.app_context():productProduct(nameTest Product,price99.99,stock10)db.session.add(product)db.session.commit()returnproductdeftest_create_order(self,client,sample_user,sample_product,auth_header):测试创建订单responseclient.post(/api/orders,json{user_id:sample_user.id,items:[{product_id:sample_product.id,quantity:2}]},headersauth_header)assertresponse.status_code201dataresponse.get_json()assertdata[total_amount]199.98# 99.99 * 2deftest_create_order_insufficient_stock(self,client,sample_user,sample_product,auth_header):测试库存不足responseclient.post(/api/orders,json{user_id:sample_user.id,items:[{product_id:sample_product.id,quantity:100}]# 库存只有10},headersauth_header)assertresponse.status_code400assert库存不足inresponse.get_json()[error]4.1.3 端到端测试层# tests/e2e/test_user_journey.py 端到端测试特点 - 慢速执行分钟级 - 完全真实环境 - 验证完整业务流程 - 模拟真实用户行为 importpytestimporttimefromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.common.keysimportKeysfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_conditionsasECpytest.fixturedefbrowser():浏览器fixtureoptionswebdriver.ChromeOptions()options.add_argument(--headless)# 无头模式options.add_argument(--no-sandbox)options.add_argument(--disable-dev-shm-usage)driverwebdriver.Chrome(optionsoptions)driver.implicitly_wait(10)yielddriver driver.quit()pytest.mark.e2eclassTestUserRegistrationJourney:用户注册完整流程测试deftest_complete_registration_flow(self,browser,live_server):完整的用户注册流程# 1. 访问首页browser.get(f{live_server}/)assert欢迎inbrowser.title# 2. 点击注册按钮register_linkbrowser.find_element(By.LINK_TEXT,注册)register_link.click()# 3. 填写注册表单username_inputbrowser.find_element(By.NAME,username)email_inputbrowser.find_element(By.NAME,email)password_inputbrowser.find_element(By.NAME,password)confirm_inputbrowser.find_element(By.NAME,confirm_password)username_input.send_keys(e2euser)email_input.send_keys(e2eexample.com)password_input.send_keys(SecurePass123!)confirm_input.send_keys(SecurePass123!)# 4. 提交表单submit_buttonbrowser.find_element(By.CSS_SELECTOR,button[typesubmit])submit_button.click()# 5. 验证注册成功WebDriverWait(browser,10).until(EC.presence_of_element_located((By.CLASS_NAME,success-message)))success_messagebrowser.find_element(By.CLASS_NAME,success-message)assert注册成功insuccess_message.text# 6. 验证用户已登录user_menubrowser.find_element(By.CLASS_NAME,user-menu)asserte2euserinuser_menu.textpytest.mark.e2eclassTestShoppingJourney:购物完整流程测试deftest_complete_shopping_flow(self,browser,live_server):完整的购物流程# 1. 登录browser.get(f{live_server}/login)browser.find_element(By.NAME,email).send_keys(testexample.com)browser.find_element(By.NAME,password).send_keys(password123)browser.find_element(By.CSS_SELECTOR,button[typesubmit]).click()# 2. 浏览商品browser.get(f{live_server}/products)product_linksbrowser.find_elements(By.CLASS_NAME,product-link)assertlen(product_links)0# 3. 查看商品详情product_links[0].click()# 4. 添加到购物车add_to_cart_buttonbrowser.find_element(By.ID,add-to-cart)add_to_cart_button.click()# 等待购物车更新time.sleep(1)# 5. 查看购物车cart_linkbrowser.find_element(By.ID,cart-link)cart_link.click()# 验证购物车有商品cart_itemsbrowser.find_elements(By.CLASS_NAME,cart-item)assertlen(cart_items)0# 6. 结账checkout_buttonbrowser.find_element(By.ID,checkout-button)checkout_button.click()# 7. 填写收货信息browser.find_element(By.NAME,address).send_keys(测试地址 123号)browser.find_element(By.NAME,phone).send_keys(13800138000)# 8. 选择支付方式并提交订单browser.find_element(By.ID,payment-method-card).click()browser.find_element(By.ID,place-order).click()# 9. 验证订单创建成功WebDriverWait(browser,10).until(EC.presence_of_element_located((By.CLASS_NAME,order-success)))success_messagebrowser.find_element(By.CLASS_NAME,order-success)assert订单创建成功insuccess_message.text4.2 测试代码组织与命名规范# 测试文件组织示例 my_project/ ├── src/ # 源代码 │ ├── models/ # 数据模型 │ │ ├── user.py │ │ └── product.py │ ├── services/ # 业务服务 │ │ ├── user_service.py │ │ └── payment_service.py │ └── api/ # API接口 │ ├── users.py │ └── products.py └── tests/ # 测试代码 ├── unit/ # 单元测试 │ ├── models/ │ │ ├── test_user.py │ │ └── test_product.py │ ├── services/ │ │ ├── test_user_service.py │ │ └── test_payment_service.py │ └── conftest.py # 单元测试共享fixture ├── integration/ # 集成测试 │ ├── api/ │ │ ├── test_users_api.py │ │ └── test_products_api.py │ ├── database/ │ │ └── test_database.py │ └── conftest.py # 集成测试共享fixture ├── e2e/ # 端到端测试 │ ├── test_user_journey.py │ ├── test_shopping_flow.py │ └── conftest.py # E2E测试共享fixture └── conftest.py # 全局共享fixture # 测试命名规范示例 测试文件命名 - test_模块名.py - 例如test_user_service.py 测试类命名 - Test类名 - 例如TestUserService 测试方法命名 - test_场景_预期结果 - 例如test_create_user_success - 例如test_login_with_invalid_credentials_should_fail fixture命名 - 使用描述性名称 - 例如user_data, database_connection - 工厂fixturemake_user, create_product 标记命名 - pytest.mark.slow - pytest.mark.integration - pytest.mark.api_v1 # 测试结构示例classTestUserService:测试类文档字符串说明测试的是什么defsetup_method(self):测试方法级别的setuppassdefteardown_method(self):测试方法级别的teardownpassdeftest_method_success_case(self): 测试方法文档字符串 Arrange: 准备测试数据 Act: 执行被测试代码 Assert: 验证结果 # Arrangeinput_data{...}expected_result{...}# Actactual_resultfunction_under_test(input_data)# Assertassertactual_resultexpected_resultdeftest_method_edge_case(self):测试边界情况passdeftest_method_error_case(self):测试错误情况pass4.3 测试数据管理策略# test_data_management.py 测试数据管理策略 1. 内联数据简单场景 2. 固定fixture共享数据 3. 数据工厂动态生成 4. 外部文件复杂数据 importpytestimportjsonimportcsvfromdataclassesimportdataclassfromtypingimportList,Dict,AnyfromfakerimportFaker# 1. 内联数据简单场景deftest_with_inline_data():内联测试数据test_cases[(1,2,3),(0,0,0),(-1,1,0),]fora,b,expectedintest_cases:assertabexpected# 2. 固定fixturepytest.fixturedefsample_user_data():固定的用户测试数据return{id:1,username:testuser,email:testexample.com,age:25,active:True}# 3. 数据工厂模式classUserFactory:用户数据工厂def__init__(self,fakerNone):self.fakerfakerorFaker()self._counter1defcreate(self,**overrides):创建用户数据user{id:self._counter,username:self.faker.user_name(),email:self.faker.email(),first_name:self.faker.first_name(),last_name:self.faker.last_name(),age:self.faker.random_int(min18,max80),active:True,created_at:self.faker.date_time_this_year()}user.update(overrides)self._counter1returnuserdefcreate_batch(self,count:int,**common_overrides):批量创建用户数据return[self.create(**common_overrides)for_inrange(count)]pytest.fixturedefuser_factory():用户工厂fixturereturnUserFactory()pytest.fixturedefadmin_user(user_factory):管理员用户returnuser_factory.create(roleadmin,is_staffTrue)pytest.fixturedefinactive_user(user_factory):非活跃用户returnuser_factory.create(activeFalse)# 4. 外部数据文件importyamlpytest.fixture(scopesession)deftest_config():从YAML文件加载测试配置withopen(tests/config/test_config.yaml,r)asf:returnyaml.safe_load(f)pytest.fixturedeftest_users_from_file():从JSON文件加载测试用户数据withopen(tests/data/users.json,r)asf:returnjson.load(f)pytest.fixturedefproduct_catalog_from_csv():从CSV文件加载产品目录products[]withopen(tests/data/products.csv,r)asf:readercsv.DictReader(f)forrowinreader:products.append({id:int(row[id]),name:row[name],price:float(row[price]),category:row[category],stock:int(row[stock])})returnproducts# 5. 参数化数据源defload_test_cases_from_json(file_path:str):从JSON文件加载测试用例withopen(file_path,r)asf:datajson.load(f)test_cases[]forcaseindata[test_cases]:test_cases.append(pytest.param(case[input],case[expected],idcase[id]))returntest_cases# 使用外部数据源进行参数化TEST_CASESload_test_cases_from_json(tests/data/calculator_cases.json)pytest.mark.parametrize(input_data,expected,TEST_CASES)deftest_with_external_data(input_data,expected):使用外部数据源进行测试# 测试逻辑pass# 6. 数据库测试数据管理classDatabaseTestData:数据库测试数据管理def__init__(self,db_session):self.dbdb_session self._cleanup_tables[]defcreate_user(self,**kwargs):创建测试用户fromapp.modelsimportUser userUser(**{username:kwargs.get(username,testuser),email:kwargs.get(email,testexample.com),password_hash:kwargs.get(password_hash,hashed_password)})self.db.add(user)self.db.commit()self._cleanup_tables.append((User,user.id))returnuserdefcreate_product(self,**kwargs):创建测试产品fromapp.modelsimportProduct productProduct(**{name:kwargs.get(name,Test Product),price:kwargs.get(price,99.99),stock:kwargs.get(stock,10)})self.db.add(product)self.db.commit()self._cleanup_tables.append((Product,product.id))returnproductdefcleanup(self):清理测试数据formodel_class,obj_idinreversed(self._cleanup_tables):objself.db.query(model_class).get(obj_id)ifobj:self.db.delete(obj)self.db.commit()self._cleanup_tables.clear()pytest.fixturedeftest_data(db_session):测试数据管理fixturedata_managerDatabaseTestData(db_session)yielddata_manager data_manager.cleanup()五、实战完整的测试项目示例5.1 项目结构ecommerce/ ├── src/ │ ├── __init__.py │ ├── models/ │ │ ├── __init__.py │ │ ├── user.py │ │ ├── product.py │ │ └── order.py │ ├── services/ │ │ ├── __init__.py │ │ ├── user_service.py │ │ ├── product_service.py │ │ ├── order_service.py │ │ └── payment_service.py │ ├── api/ │ │ ├── __init__.py │ │ ├── users.py │ │ ├── products.py │ │ └── orders.py │ └── database.py ├── tests/ │ ├── unit/ │ │ ├── models/ │ │ │ ├── test_user.py │ │ │ ├── test_product.py │ │ │ └── test_order.py │ │ ├── services/ │ │ │ ├── test_user_service.py │ │ │ ├── test_product_service.py │ │ │ ├── test_order_service.py │ │ │ └── test_payment_service.py │ │ └── conftest.py │ ├── integration/ │ │ ├── api/ │ │ │ ├── test_users_api.py │ │ │ ├── test_products_api.py │ │ │ └── test_orders_api.py │ │ ├── database/ │ │ │ └── test_database_integration.py │ │ └── conftest.py │ ├── e2e/ │ │ ├── test_user_registration_flow.py │ │ ├── test_shopping_flow.py │ │ └── conftest.py │ └── conftest.py ├── pytest.ini ├── requirements.txt └── README.md5.2 持续集成配置# .github/workflows/test.ymlname:Testson:push:branches:[main,develop]pull_request:branches:[main]jobs:test:runs-on:ubuntu-lateststrategy:matrix:python-version:[3.8,3.9,3.10]services:postgres:image:postgres:13env:POSTGRES_USER:test_userPOSTGRES_PASSWORD:test_passwordPOSTGRES_DB:test_dboptions:---health-cmd pg_isready--health-interval 10s--health-timeout 5s--health-retries 5ports:-5432:5432redis:image:redis:6-alpineoptions:---health-cmd redis-cli ping--health-interval 10s--health-timeout 5s--health-retries 5ports:-6379:6379steps:-uses:actions/checkoutv3-name:Set up Python ${{matrix.python-version}}uses:actions/setup-pythonv4with:python-version:${{matrix.python-version}}-name:Install dependenciesrun:|python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-cov pytest-xdist pip install flake8 black mypy-name:Lint with flake8run:|flake8 src/ --count --selectE9,F63,F7,F82 --show-source --statistics flake8 src/ --count --exit-zero --max-complexity10 --max-line-length88 --statistics-name:Check formatting with blackrun:|black --check src/ tests/-name:Type check with mypyrun:|mypy src/ --ignore-missing-imports-name:Run unit testsrun:|pytest tests/unit/ -v --covsrc --cov-reportxml --cov-reporthtml-name:Run integration testsenv:DATABASE_URL:postgresql://test_user:test_passwordlocalhost:5432/test_dbREDIS_URL:redis://localhost:6379/0run:|pytest tests/integration/ -v --covsrc --cov-append-name:Upload coverage to Codecovuses:codecov/codecov-actionv3with:file:./coverage.xmlflags:unittests-name:Upload test resultsif:always()uses:actions/upload-artifactv3with:name:test-results-${{matrix.python-version}}path:|htmlcov/ .coverage5.3 性能测试示例# tests/performance/test_performance.pyimportpytestimporttimeimportstatisticsfromdatetimeimportdatetimepytest.mark.performanceclassTestPerformance:性能测试pytest.fixturedeflarge_dataset(self):大型数据集return[iforiinrange(10000)]deftest_sort_performance(self,large_dataset,benchmark):排序性能测试# 使用pytest-benchmark插件resultbenchmark(sorted,large_dataset)assertlen(result)len(large_dataset)deftest_database_query_performance(self,database,benchmark):数据库查询性能测试# 准备测试数据fromsrc.modelsimportUserfromsrc.databaseimportdb# 插入1000个测试用户users[]foriinrange(1000):userUser(usernamefuser_{i},emailfuser_{i}example.com)users.append(user)db.session.bulk_save_objects(users)db.session.commit()# 性能测试查询所有用户defquery_users():returnUser.query.all()resultbenchmark(query_users)assertlen(result)1000deftest_api_response_time(self,client,benchmark):API响应时间测试defcall_api():responseclient.get(/api/users)assertresponse.status_code200returnresponse resultbenchmark(call_api)# 验证响应时间在可接受范围内statsresult.statsassertstats[mean]0.1# 平均响应时间小于100mspytest.mark.loaddeftest_concurrent_requests(self):并发请求测试importthreadingimportqueue resultsqueue.Queue()defmake_request(request_id):模拟请求start_timetime.time()# 模拟API调用time.sleep(0.01)end_timetime.time()results.put((request_id,end_time-start_time))# 创建100个并发请求threads[]foriinrange(100):threadthreading.Thread(targetmake_request,args(i,))threads.append(thread)thread.start()# 等待所有线程完成forthreadinthreads:thread.join()# 收集结果response_times[]whilenotresults.empty():_,response_timeresults.get()response_times.append(response_time)# 分析结果avg_timestatistics.mean(response_times)max_timemax(response_times)print(f\n并发测试结果:)print(f 请求数量:{len(response_times)})print(f 平均响应时间:{avg_time:.3f}s)print(f 最大响应时间:{max_time:.3f}s)# 断言assertavg_time0.02# 平均响应时间小于20msassertmax_time0.05# 最大响应时间小于50mspytest.mark.stressdeftest_stress_test(self):压力测试importrandomimportconcurrent.futuresdefstress_function(data):压力测试函数# 模拟CPU密集型操作result0foriinrange(1000):resultdata[i%len(data)]*random.random()returnresult# 准备测试数据test_data[random.random()for_inrange(10000)]# 使用线程池进行压力测试start_timetime.time()withconcurrent.futures.ThreadPoolExecutor(max_workers50)asexecutor:futures[executor.submit(stress_function,test_data)for_inrange(1000)]# 收集结果results[]forfutureinconcurrent.futures.as_completed(futures):results.append(future.result())end_timetime.time()durationend_time-start_time requests_per_second1000/durationprint(f\n压力测试结果:)print(f 总请求数: 1000)print(f 总时间:{duration:.2f}s)print(f 吞吐量:{requests_per_second:.1f}请求/秒)# 验证assertduration10# 总时间小于10秒assertrequests_per_second100# 吞吐量大于100请求/秒六、总结与面试准备6.1 测试知识体系总结测试技能矩阵技能等级unittest掌握程度pytest掌握程度测试策略能力实战经验初级基本用法基本用法编写简单单元测试个人项目中级Mock/高级特性Fixture/参数化集成测试设计团队项目高级自定义扩展插件开发完整测试体系大型项目专家框架贡献生态建设测试架构设计开源项目测试覆盖率目标项目类型 单元测试覆盖率 集成测试覆盖率 端到端测试覆盖率 ------------------------------------------------------------------------ 工具库 90% 60% 20% Web应用 80% 70% 40% API服务 85% 75% 30% 数据管道 70% 80% 50% 机器学习 60% 70% 60%6.2 面试高频问题深度解析Q1unittest和pytest的主要区别是什么如何选择技术对比 核心区别 1. 语法 unittest基于类需要继承TestCase pytest基于函数使用原生assert 2. 生态系统 unittestPython标准库稳定但功能有限 pytest第三方丰富的插件生态 3. 高级特性 unittestMock集成好 pytestFixture系统强大参数化灵活 4. 学习曲线 unittest简单直观 pytest功能强大但学习成本稍高 选择建议 1. 新项目优先选择pytest 2. 维护老项目沿用现有框架 3. 需要复杂测试pytest更合适 4. 简单单元测试两者都可以 Q2如何测试包含外部API调用的代码解决方案importpytestfromunittest.mockimportMock,patchimportrequestsclassTestExternalAPI:外部API测试策略deftest_with_direct_mock(self):直接Mock requestswithpatch(requests.get)asmock_get:# 配置Mock响应mock_responseMock()mock_response.status_code200mock_response.json.return_value{data:test}mock_get.return_valuemock_response# 调用被测试代码responserequests.get(https://api.example.com/data)dataresponse.json()# 验证assertdata[data]testmock_get.assert_called_once_with(https://api.example.com/data)deftest_with_responses_library(self):使用responses库专门用于HTTP测试importresponsesresponses.activatedeftest_api_call():# 模拟特定URL的响应responses.add(responses.GET,https://api.example.com/users/1,json{id:1,name:John},status200)# 调用被测试代码responserequests.get(https://api.example.com/users/1)dataresponse.json()assertdata[name]Johnassertlen(responses.calls)1test_api_call()deftest_with_vcrpy(self):使用vcrpy记录和回放HTTP请求importvcr# 第一次运行会记录请求之后回放vcr.use_cassette(fixtures/vcr_cassettes/api_call.yaml)deftest_with_real_api():responserequests.get(https://api.example.com/data)assertresponse.status_code200test_with_real_api()pytest.mark.integrationdeftest_with_real_api(self):真正的集成测试谨慎使用# 只在CI环境或明确标记时运行responserequests.get(https://api.example.com/health,timeout5)assertresponse.status_code200deftest_circuit_breaker_pattern(self):测试熔断器模式fromcircuitbreakerimportcircuitcircuit(failure_threshold5,recovery_timeout60)defunreliable_api_call():# 模拟可能失败的外部调用responserequests.get(https://unreliable-api.com/data)response.raise_for_status()returnresponse.json()# 测试正常情况withpatch(requests.get)asmock_get:mock_get.return_value.status_code200mock_get.return_value.json.return_value{status:ok}resultunreliable_api_call()assertresult[status]ok# 测试熔断触发withpatch(requests.get)asmock_get:mock_get.side_effectrequests.exceptions.RequestException# 连续失败5次应该触发熔断foriinrange(5):withpytest.raises(requests.exceptions.RequestException):unreliable_api_call()# 第6次应该直接抛出CircuitBreakerErrorfromcircuitbreakerimportCircuitBreakerErrorwithpytest.raises(CircuitBreakerError):unreliable_api_call()Q3如何设计可测试的代码设计原则与示例 可测试代码设计原则 1. 单一职责每个函数/类只做一件事 2. 依赖注入通过参数传递依赖而不是硬编码 3. 接口隔离依赖抽象而非具体实现 4. 避免全局状态使测试可重复 # 不可测试的代码示例classHardToTestUserService:难以测试的代码def__init__(self):# 硬编码依赖self.dbDatabase()# 直接创建难以Mockself.cacheRedisCache()# 直接创建defget_user(self,user_id):# 直接使用全局配置timeoutConfig.get(db_timeout)# 全局状态# 方法职责过多userself.db.query(fSELECT * FROM users WHERE id {user_id})ifuser:# 业务逻辑与缓存逻辑耦合self.cache.set(fuser:{user_id},user)returnuser# 可测试的代码示例fromabcimportABC,abstractmethodfromtypingimportOptional,Dict,Any# 定义抽象接口classDatabase(ABC):abstractmethoddefexecute(self,query:str,params:tuple)-Optional[Dict]:passclassCache(ABC):abstractmethoddefset(self,key:str,value:Any,ttl:intNone)-bool:passabstractmethoddefget(self,key:str)-Optional[Any]:pass# 可测试的服务类classTestableUserService:易于测试的代码def__init__(self,db:Database,cache:Cache,timeout:int30):# 通过依赖注入接收依赖self.dbdb self.cachecache self.timeouttimeoutdefget_user(self,user_id:int)-Optional[Dict]:获取用户信息# 先尝试从缓存获取cached_userself._get_user_from_cache(user_id)ifcached_user:returncached_user# 从数据库获取userself._get_user_from_db(user_id)# 缓存结果ifuser:self._cache_user(user_id,user)returnuserdef_get_user_from_cache(self,user_id:int)-Optional[Dict]:从缓存获取用户可单独测试try:returnself.cache.get(fuser:{user_id})exceptException:# 缓存失败不影响主要功能returnNonedef_get_user_from_db(self,user_id:int)-Optional[Dict]:从数据库获取用户可单独测试try:resultself.db.execute(SELECT * FROM users WHERE id %s,(user_id,))returnresultexceptExceptionase:# 记录日志但不暴露内部细节raiseValueError(f获取用户失败:{user_id})fromedef_cache_user(self,user_id:int,user:Dict)-bool:缓存用户信息可单独测试try:returnself.cache.set(fuser:{user_id},user,ttl3600)exceptException:returnFalse# 测试示例deftest_testable_user_service():测试可测试的用户服务# 创建Mock依赖mock_dbMock(specDatabase)mock_cacheMock(specCache)# 配置Mock行为mock_cache.get.return_valueNone# 缓存未命中mock_db.execute.return_value{id:1,name:Alice}mock_cache.set.return_valueTrue# 创建服务实例serviceTestableUserService(dbmock_db,cachemock_cache)# 测试userservice.get_user(1)# 验证assertuser[name]Alicemock_db.execute.assert_called_once_with(SELECT * FROM users WHERE id %s,(1,))mock_cache.set.assert_called_once()Q4如何处理测试中的随机性和不确定性解决方案importpytestimportrandomimporttimefromdatetimeimportdatetimefromunittest.mockimportpatchclassTestRandomness:处理随机性和不确定性的策略deftest_with_fixed_seed(self):使用固定随机种子# 设置随机种子确保可重复random.seed(42)# 生成随机数numbers1[random.random()for_inrange(5)]# 重置种子应该得到相同序列random.seed(42)numbers2[random.random()for_inrange(5)]assertnumbers1numbers2deftest_with_mocked_random(self):Mock随机函数withpatch(random.random)asmock_random:# 控制随机返回值mock_random.return_value0.5resultrandom.random()assertresult0.5deftest_time_based_logic(self):测试时间相关逻辑# 方法1Mock时间withpatch(datetime.datetime)asmock_datetime:fixed_timedatetime(2023,1,1,12,0,0)mock_datetime.now.return_valuefixed_time current_timedatetime.now()assertcurrent_timefixed_time# 方法2使用时间冻结库try:fromfreezegunimportfreeze_timefreeze_time(2023-01-01 12:00:00)deftest_frozen_time():assertdatetime.now()datetime(2023,1,1,12,0,0)test_frozen_time()exceptImportError:passdeftest_concurrent_behavior(self):测试并发行为importthreading results[]lockthreading.Lock()defappend_with_delay(value):模拟不确定的执行时间time.sleep(random.random()*0.01)# 随机延迟withlock:results.append(value)# 创建多个线程threads[]foriinrange(10):threadthreading.Thread(targetappend_with_delay,args(i,))threads.append(thread)# 启动所有线程forthreadinthreads:thread.start()# 等待所有线程完成forthreadinthreads:thread.join()# 验证结果不关心顺序只关心内容assertset(results)set(range(10))assertlen(results)10deftest_flaky_test_retry(self):处理不稳定的测试# 方法1使用pytest的重试插件# 安装pip install pytest-rerunfailures# 运行pytest --reruns 3 --reruns-delay 1# 方法2手动实现重试逻辑max_retries3retry_count0whileretry_countmax_retries:try:# 执行可能不稳定的操作resultself._unstable_operation()assertresultexpected_valuebreak# 成功则退出循环exceptAssertionErrorase:retry_count1ifretry_countmax_retries:raise# 重试次数用完抛出异常time.sleep(1)# 等待后重试def_unstable_operation(self):模拟不稳定的操作# 模拟90%的成功率ifrandom.random()0.9:returnsuccesselse:raiseException(随机失败)deftest_probabilistic_assertions(self):概率性断言# 测试随机算法的统计特性# 生成大量样本samples[random.random()for_inrange(10000)]# 验证统计特性meansum(samples)/len(samples)assert0.49mean0.51# 平均值应该在0.5附近# 验证分布less_than_halfsum(1forxinsamplesifx0.5)proportionless_than_half/len(samples)assert0.48proportion0.52# 大约一半小于0.5pytest.mark.stressdeftest_deterministic_under_stress(self):压力下的确定性测试# 在压力下验证系统行为仍然确定importconcurrent.futuresdefstress_task(task_id):压力任务# 使用固定种子确保每个任务可重复random.seed(task_id)# 执行一些随机操作results[]for_inrange(100):results.append(random.randint(1,100))returnsum(results)# 并发执行withconcurrent.futures.ThreadPoolExecutor(max_workers10)asexecutor:futures[executor.submit(stress_task,i)foriinrange(100)]results[f.result()forfinfutures]# 验证相同输入应该得到相同输出# 重新运行应该得到相同结果random.seed(0)expected_results[]foriinrange(100):random.seed(i)results_i[]for_inrange(100):results_i.append(random.randint(1,100))expected_results.append(sum(results_i))assertresultsexpected_results6.3 实战面试题题目设计一个电商购物车的测试方案 需求分析 1. 添加商品到购物车 2. 从购物车移除商品 3. 更新商品数量 4. 计算总价考虑折扣、税费 5. 库存验证 6. 清空购物车 7. 持久化存储 测试方案 # 1. 单元测试 - 购物车核心逻辑classTestShoppingCartUnit:购物车单元测试deftest_add_item(self):测试添加商品cartShoppingCart()cart.add_item(product_1,2,10.0)assertcart.get_item_count()1assertcart.get_total()20.0deftest_remove_item(self):测试移除商品cartShoppingCart()cart.add_item(product_1,2,10.0)cart.remove_item(product_1)assertcart.get_item_count()0assertcart.get_total()0.0deftest_update_quantity(self):测试更新数量cartShoppingCart()cart.add_item(product_1,2,10.0)cart.update_quantity(product_1,5)assertcart.get_quantity(product_1)5assertcart.get_total()50.0deftest_calculate_total_with_discount(self):测试折扣计算cartShoppingCart(discount_rate0.1)# 10%折扣cart.add_item(product_1,2,10.0)assertcart.get_total()18.0# 20 * 0.9deftest_calculate_total_with_tax(self):测试税费计算cartShoppingCart(tax_rate0.08)# 8%税费cart.add_item(product_1,2,10.0)assertcart.get_total()21.6# 20 * 1.08deftest_stock_validation(self):测试库存验证inventoryMockInventory()inventory.get_stock.return_value5cartShoppingCart(inventoryinventory)# 添加不超过库存的数量cart.add_item(product_1,3,10.0)assertcart.get_quantity(product_1)3# 尝试添加超过库存的数量withpytest.raises(OutOfStockError):cart.add_item(product_1,3,10.0)# 总共需要6但库存只有5# 2. 集成测试 - 购物车与数据库/API集成classTestShoppingCartIntegration:购物车集成测试deftest_cart_persistence(self,db_session):测试购物车持久化# 创建购物车cartShoppingCart()cart.add_item(product_1,2,10.0)# 保存到数据库cart_idcart.save_to_db(db_session)# 从数据库加载loaded_cartShoppingCart.load_from_db(db_session,cart_id)# 验证assertloaded_cart.get_item_count()1assertloaded_cart.get_total()20.0deftest_cart_with_real_inventory(self,inventory_service):测试真实库存服务cartShoppingCart(inventoryinventory_service)# 先检查库存stockinventory_service.get_stock(product_1)ifstock0:# 添加商品cart.add_item(product_1,min(2,stock),10.0)assertcart.get_item_count()1else:# 库存不足withpytest.raises(OutOfStockError):cart.add_item(product_1,1,10.0)deftest_checkout_process(self,cart_service,payment_service,inventory_service):测试完整结账流程# 创建购物车并添加商品cartcart_service.create_cart()cart_service.add_item(cart.id,product_1,2)# 结账ordercart_service.checkout(cart_idcart.id,payment_methodcredit_card,shipping_address123 Test St)# 验证订单创建assertorder.statuspaidassertorder.total_amount0# 验证库存已扣减new_stockinventory_service.get_stock(product_1)assertnew_stockoriginal_stock-2# 3. 性能测试classTestShoppingCartPerformance:购物车性能测试deftest_large_cart_performance(self,benchmark):测试大型购物车性能cartShoppingCart()defadd_many_items():foriinrange(1000):cart.add_item(fproduct_{i},1,i1)returncart.get_total()resultbenchmark(add_many_items)assertresultsum(range(1,1001))deftest_concurrent_cart_access(self):测试并发访问cartShoppingCart()cart.add_item(popular_product,100,10.0)defupdate_cart(thread_id):模拟多个用户同时更新购物车# 每个线程减少1个数量current_qtycart.get_quantity(popular_product)ifcurrent_qty0:cart.update_quantity(popular_product,current_qty-1)# 创建多个线程同时更新threads[]foriinrange(50):threadthreading.Thread(targetupdate_cart,args(i,))threads.append(thread)# 启动所有线程forthreadinthreads:thread.start()# 等待完成forthreadinthreads:thread.join()# 验证最终数量final_qtycart.get_quantity(popular_product)assertfinal_qty50# 应该剩下至少50个# 4. 端到端测试classTestShoppingCartE2E:购物车端到端测试deftest_complete_shopping_flow(self,browser,live_server):完整购物流程# 1. 用户登录browser.get(f{live_server}/login)# ... 登录操作# 2. 浏览商品并添加到购物车browser.get(f{live_server}/products)add_buttonsbrowser.find_elements(By.CLASS_NAME,add-to-cart)add_buttons[0].click()# 3. 查看购物车browser.get(f{live_server}/cart)cart_itemsbrowser.find_elements(By.CLASS_NAME,cart-item)assertlen(cart_items)0# 4. 更新数量quantity_inputbrowser.find_element(By.NAME,quantity)quantity_input.clear()quantity_input.send_keys(3)update_buttonbrowser.find_element(By.CLASS_NAME,update-quantity)update_button.click()# 5. 结账checkout_buttonbrowser.find_element(By.ID,checkout)checkout_button.click()# 6. 填写收货和支付信息# ... 填写表单# 7. 提交订单submit_buttonbrowser.find_element(By.ID,submit-order)submit_button.click()# 8. 验证订单成功success_messagebrowser.find_element(By.CLASS_NAME,order-success)assert订单创建成功insuccess_message.text# 9. 验证购物车已清空browser.get(f{live_server}/cart)empty_messagebrowser.find_element(By.CLASS_NAME,cart-empty)assert购物车为空inempty_message.text6.4 学习路线与资源推荐学习路线图推荐资源官方文档unittest官方文档pytest官方文档coverage.py经典书籍《Python Testing with pytest》《Test-Driven Development with Python》《Python测试之道》在线课程Coursera: Testing and Monitoring in PythonUdemy: Python Testing with Pytest极客时间: Python自动化测试实战工具推荐测试框架pytest, unittestMock库unittest.mock, pytest-mock覆盖率coverage.py, pytest-covWeb测试Selenium, PlaywrightAPI测试requests, httpx, fastapi-testclient性能测试locust, pytest-benchmark持续集成GitHub Actions, GitLab CI, Jenkins实战项目为开源项目贡献测试用例搭建个人项目的完整测试套件实现测试框架的定制插件面试准备清单理论准备掌握单元测试、集成测试、端到端测试的区别理解测试金字塔和测试策略熟悉FIRST原则和测试设计模式技术准备熟练使用unittest和pytest掌握Mock和测试隔离技巧了解测试覆盖率和质量指标熟悉持续集成和自动化测试项目经验准备2-3个测试相关的项目经验能够描述测试框架的选型和设计能够分析测试失败的原因和解决方案思维准备培养测试驱动开发的思维学习如何设计可测试的代码掌握测试用例的设计方法理解测试在DevOps中的作用最后的建议测试不仅是技术更是一种思维方式。优秀的开发者应该具备测试思维在编写代码时就能预见可能的测试场景。在面试中展示你对测试的深刻理解比展示你记住了多少API更有价值。记住好的测试不是追求100%的覆盖率而是用合适的测试策略在开发效率和质量保障之间找到最佳平衡点。祝你在面试中展现出卓越的测试素养获得心仪的Offer
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

外包公司设计完网站谁负责运营wordpress写作主题

第一章:Open-AutoGLM 2.0 缺陷全景透视Open-AutoGLM 2.0 作为开源自动化大语言模型生成框架,在提升开发效率的同时,暴露出若干关键缺陷。这些缺陷不仅影响系统稳定性,还可能引发安全风险与性能瓶颈。架构耦合度过高 系统核心模块间…

张小明 2026/1/12 16:16:06 网站建设

要建设一个网站需要什么手续费福州做网站互联网公司排名

开源大模型新星:FLUX.1-dev镜像助力高精度文生图应用落地 在数字内容爆发式增长的今天,AI生成图像已不再是实验室里的概念——从社交媒体配图到游戏原画设计,从广告创意到虚拟偶像制作,高质量、可控性强的文生图技术正成为生产力工…

张小明 2026/1/5 12:37:41 网站建设

三亚市建设局网站公示下载新华社app

在如今数据驱动的时代,数据库的性能和可靠性是企业成功的关键因素之一。如何优化查询速度和确保数据的一致性是许多开发人员和数据库管理员面临的重要任务。尽管YashanDB在多个方面表现优秀,用户反馈和建议可以为后续的版本改进提供参考,帮助…

张小明 2026/1/8 20:42:13 网站建设

wordpress使用七牛图像服务网页怎么优化

#光伏并网模型PV2G PWM调制,实现光伏并网,参数修改方便,需要什么版本的提前说哦,未说明版本直接发是2018a版和2023b版本光伏并网建模这事,搞过电力电子仿真的同学应该都不陌生。今天咱们拿PV2G模型开刀,重点…

张小明 2026/1/6 13:45:47 网站建设

浏览器打开网站网页制作模板html

使用Python创建太空入侵者游戏:从基础到动画与音效 在Python编程领域,创建游戏是一项既有趣又富有挑战性的任务。本文将详细介绍如何使用Python和Pygame库创建一个太空入侵者游戏(Pivaders),并逐步添加动画和音效,让游戏更加生动有趣。 1. 游戏基础功能实现 首先,我们…

张小明 2026/1/6 12:47:07 网站建设

分享类网站怎么做软件开发培训学校的三大特色

还在为无法保存小红书精彩内容而烦恼吗?每次看到心动的穿搭分享、实用的生活技巧,却只能眼睁睁看着它们消失在信息流中?今天为你揭秘一款免费开源工具——XHS-Downloader,让你轻松实现小红书无水印批量下载,无论是图文…

张小明 2026/1/7 8:01:56 网站建设