首页 / 资讯中心 / 文章详情

PyCharm中运行pytest:右键运行与解释器运行的区别与最佳实践

PyCharm中运行pytest:右键运行与解释器运行的区别与最佳实践 ★ FEATURED ARTICLE
很多人写了几十上百条测试用例但真到要跑的时候反而卡住了——不是用例本身有问题而是“用什么方式去跑”这件事没理顺。尤其是在 PyCharm 里点绿色箭头、用 pytest 解释器直接执行这两种路径看起来都在运行用例实际背后的机制差别很大。理解透了你才能在排查失败用例、搞 CI 流水线、处理跨环境执行的时候不抓瞎。这篇文章就把这两种运行方式掰开揉碎讲清楚从环境配置到命令参数再到各种奇怪的坑一次说透。1. 弄懂两种运行方式的底层逻辑再动手1.1 PyCharm 运行用例并不是“替你执行 pytest”很多初学者有个误区觉得在 PyCharm 里右键运行测试用例就等同于在终端里敲 pytest。实际上 PyCharm 只是替你组装了一条 pytest 命令然后在一个内置的测试运行器里展示结果。这个“组装”过程里藏着许多变量项目解释器选的是哪一个、默认测试运行器是 pytest 还是 unittest、运行配置里的目标选的是文件还是类还是函数。只要其中一个环节跟你预想的不一样就会出现“明明 pytest 能跑PyCharm 里却报错或者直接提示没有找到测试”的怪事。我见过一个真实场景某开发者用 PyCharm 打开同事的项目右键想跑某个测试文件结果 PyCharm 提示“No tests were found”但他在终端里执行 pytest 明明能收集到几十个用例。查了半天最后发现是 PyCharm 的默认测试运行器被设置成了 unittest导致它按照 unittest 的规则去收集测试自然什么都找不到。所以第一步不是急着点按钮而是先确认 PyCharm 的测试框架设置。1.2 pytest 解释器运行的本质是“用 Python 进程加载 pytest 再执行用例”另一种常见方式是配置一个 pytest 解释器作为运行入口。这其实是在 PyCharm 的 Run/Debug Configuration 里新建一个 Python 运行配置把模块名填成 pytest然后由 Python 解释器直接启动 pytest 模块。这样做的好处是你可以像控制命令行 pytest 一样往参数列表里塞各种选项比如 -s、-v、-k、--maxfail执行逻辑跟终端完全一致而且 PyCharm 还能帮你展示测试树和失败堆栈。理解了这两个底层逻辑后面遇到问题就不会瞎猜了。PyCharm 的右键运行本质上是“一个封装好的、参数被限制的 pytest 启动器”而你手写一个 pytest 解释器运行配置则相当于“把终端的灵活性搬进了 IDE”。两种方式不是替代关系而是互补关系——日常写代码用前者图快排查复杂问题用后者图全。2. 配置这一步做不对后面全是无用功2.1 项目解释器与虚拟环境的选择无论你用哪种运行方式一切的前提都是解释器正确。我强烈建议每个项目都使用虚拟环境而不是直接用全局 Python。原因很简单不同项目依赖的 pytest 版本可能不同插件集也不同混在全局环境里迟早会出现“A 项目能跑 B 项目跑不了”的情况。在 PyCharm 里设置解释器的路径是File → Settings → Project → Python Interpreter。这里的重点不是选一个解释器就完了而是要逐项确认当前用的 Python 版本是否满足 pytest 的兼容要求、环境里是否装了 pytest 相关插件比如 pytest-xdist、pytest-cov、pip 列表里有没有重复安装的同名包。如果项目从别人手里交接过来不要信任锁文件里声称的版本号直接在终端执行pip list | grep pytest看实际版本。提示创建虚拟环境时建议用python -m venv .venv而不是 PyCharm 自带的 New Virtualenv。后者有时候会继承全局 site-packages导致一个“看似干净、实则搀了杂质”的环境。顺手把项目根目录下的.venv文件夹加进.gitignore免得哪天手滑提交进去。2.2 把 PyCharm 的默认测试运行器切到 pytest我遇到过不少朋友卡在这样一个问题里右键测试文件时PyCharm 弹出的运行菜单里根本没有“pytest”相关选项而是显示“Run unittest”或者干脆只让你运行某个 Python 脚本。这是因为 PyCharm 默认的测试运行器不一定是你想要的。修改方法很简单Settings → Tools → Python Integrated Tools → Testing → Default test runner把它从 unittest 或 pytest 中选择为 pytest。改完这一步右键测试文件时菜单里就会出现“Run pytest in [文件名]”的选项绿色箭头旁边也会多出 pytest 图标。这里有一个细节很多人忽略即便你把默认测试运行器改成了 pytest某些老旧项目里如果存在unittest.TestCase子类PyCharm 收集用例时依然会优先继承 unittest 的逻辑去跑里面test_开头的方法。这不是 bug而是 pytest 框架本身为了兼容 unittest 特意保留了一个 hook。你要是想让某个类彻底按 pytest 的 fixture 和断言方式执行就别让它继承unittest.TestCase如果必须继承也要清楚它的setUp和tearDown生命周期跟 pytest 的setup_method并不完全相同。2.3 建一个专属的 pytest 运行配置PyCharm 的右键运行虽然方便但它能控制的参数很少。想用-k做关键字筛选用例、想加-m标记跳过某些类、想输出-s打印日志右键菜单根本不给机会。这时候就需要手动创建运行配置。步骤是Run → Edit Configurations → 左上角加号 → 选 Python → 在 Configuration 标签页里Script path 换成 Module模块名填pytest。Parameters 里你可以填上你想要的所有参数比如-v -s --tbshort tests/test_user_login.pyWorking directory 建议填$PROJECT_DIR$这样 pytest 的 rootdir 会锚定在项目根目录目录结构相关的相对定位才不会错。如果项目里存在多个 tests 目录或配置文件不在根目录这个 Working directory 更要仔细设置很多“找不到 conftest.py”“fixture 找不到”的问题就是从这里来的。创建好运行配置后建议给配置取一个跟项目阶段匹配的名字比如“all tests - dev”“smoke tests only”。配合 PyCharm 右上角的下拉框可以一键在不同配置之间切换比每次改参数和右键点来点去高效得多。3. PyCharm 右键运行用例的全部细节3.1 三种右键目标函数、类、文件PyCharm 的右键运行支持三个层级函数、类、文件。分别对应你要跑“刚写的一条断言逻辑”“一个模块内的所有用例”还是“统计一个文件里全部用例的回归验证”。右键某个test_xxx函数时PyCharm 生成的 pytest 命令大致等价于pytest tests/test_user_login.py::test_success_login右键某个TestLogin类时大致等价于pytest tests/test_user_login.py::TestLogin右键整个文件时大致等价于pytest tests/test_user_login.py注意这里说的“大致等价”并不准确。PyCharm 生成的命令还会包含--no-header、--no-summary等附加参数以及一套它内部的展示插件。所以你在终端里跑同样的命令输出风格是不一样的。这点要在心里有数否则你会在终端里看到跟 PyCharm 不一样的结果格式误以为是执行逻辑出了问题。3.2 右键运行常见的“找不到测试”问题右键运行最让人崩溃的报错就是“No tests were found”。这个问题的原因可能有几种文件名不符合 pytest 的收集规则。pytest 默认只收集文件名以test_开头或_test结尾的文件。你写了个login_test.py终端 pytest 可能还能靠配置的 python_files 规则识别但 PyCharm 的右键逻辑不总会主动去读这些自定义配置所以建议命名规范从源头就统一成test_前缀。函数名不符合收集规则。pytest 只收集test_开头的函数相对于 def 不是类内方法时。你定义了一个名字叫check_user_001()的函数哪怕里面写满了断言PyCharm 也不会认为它是一个用例。类名不符合收集规则。类的收集规则是名称以 Test 开头、且不能有__init__方法。注意这个限制比很多人想象得严格——Test_Login这种带下划线的类名pytest 是默认收集不到的。conftest.py 报错导致收集中断。如果项目根目录或测试目录下存在 conftest.py而它加载时报错比如导入一个不存在的模块pytest 的收集过程会直接中断结果就是明明文件里写着 test 函数PyCharm 却提示找不到测试。注意排查这类问题时最快的方法是先在终端跑到同样的 pytest 命令。终端能收集到而 PyCharm 不行问题大概率出在 PyCharm 的运行配置或默认测试运行器上终端也收集不到那问题就是项目代码本身不符合 pytest 的规约跟 IDE 没关系。3.3 并行调试与失败用例的单独重跑开发调试用例的时候我不建议一上来就跑整个文件。印象最深刻的一次教训是某次我在一个测试文件里加了断点右键跑整个文件结果前十几个用例都成功最后一条用例的参数化数据里有一个异常的大对象导致测试进程直接卡死。PyCharm 显示出来全是“running”状态完全分不清卡在哪一条。正确的做法是先用鼠标右键精确运行出问题的那个用例函数。如果不行再用-k关键字把范围缩小比如pytest tests/test_order.py -k refund这样只跑函数名里带 refund 的用例能极大缩小问题范围。PyCharm 的 Run 窗口里也能直接右键某一条用例选择 Run本质上等价于定位到函数再运行。另一个实用技巧PyCharm 的测试结果面板底部有一个“More”按钮或一个范围选择下拉框可以选择 Re-run failed tests。失败用例的重跑跟从头跑全部不同它只执行刚才失败的用例而不是把整个套件再跑一遍。这个在修复 bug 后验证的时候特别省时间。4. pytest 解释器运行命令行的控制力是右键运行比不了的4.1 创建“pytest 解释器”型运行配置的完整步骤点击 Run → Edit Configurations新增一个 Python 配置把 Script path 换成 Module name具体填pytest。这一步的本质是用 python 解释器去执行python -m pytest。我推荐使用这种形式而不是直接填 pytest 可执行文件的路径。原因很直接你填 pytest 路径等于直接执行虚拟环境里的一个 bin 脚本脚本顶部的 shebang 指向的解释器跟你在 PyCharm 里选的项目解释器可能不是同一个而用python -m pytest则必然是当前解释器去加载 pytest 模块依赖查找路径与你预期的完全一致。配置填完之后Parameters 区域大有用处。我把常用组合整理成一张表方便你抄作业参数组合使用场景-v -s --tbshort日常调试显示每条用例全名、输出捕获的打印日志、短路堆栈-q --tblineCI 快跑场景安静模式每行只显示一个点和一个错误摘要-k login or order只跑名称匹配 login 或 order 的函数-m smoke只跑打了smoke标记的用例--maxfail1遇到第一条失败就停止适合快速确认是否核心逻辑挂了--lf只跑上次失败的用例修复后的回归首选--durations5展示最慢的 5 条用例耗时定位性能瓶颈4.2 用节点标识符精确圈定用例pytest 支持用“节点 ID”语法指定用例范围这是命令行和 PyCharm 右键运行都很实用的能力。所谓节点 ID就是用冒号分隔的文件路径加类名加函数名。例如pytest tests/test_user.py::TestRegister::test_register_with_email这条命令只执行TestRegister类里面的test_register_with_email这一个函数。如果你觉得打字太长还可以用模糊匹配pytest tests/test_user.py -k register and not email这里的and、not是-k表达式支持的逻辑关键字。需要注意-k匹配的是测试节点的名称包括类名和函数名不是文件路径记号别弄混。还有一个隐蔽但好用的点-k也支持匹配参数化生成的用例 ID。假设你的用例是这样写的pytest.mark.parametrize(env, [dev, staging, prod]) def test_env_deploy(env): ...收集后生成的节点名是test_env_deploy[dev]、test_env_deploy[staging]、test_env_deploy[prod]。用-k staging就能精准跑出 staging 环境的那条这在多环境测试时特别省事。4.3 pytest.ini / pyproject.toml 配置文件决定默认行为运行 pytest 时配置文件决定了大量“默认值”。你可以定义一个pytest.ini或pyproject.toml里的[tool.pytest.ini_options]来固化常用行为例如[pytest] testpaths tests addopts -v -s --maxfail3 filterwarnings ignore::DeprecationWarning markers smoke: 冒烟测试标记 regression: 回归测试标记配置文件的优先级是从上到下的pytest.ini 的优先级高于 pyproject.toml。多个规则同时存在时具体以命令行为最高优先级。有一点比较坑如果你在addopts里写死了-v然后在 PyCharm 的右键运行里再加参数它会在已有参数后面追加。也就是说 PyCharm 里的“额外参数”只是延长了命令而不是覆盖默认值。遇到输出异常冗长的情况优先检查是不是addopts里有-vv或者-s这类全局开关。4.4 解释器运行与终端运行的性能差异有人会问“在 PyCharm 里配置 pytest 解释器运行跟我在终端里敲 pytest效果到底一不一样”执行引擎是一样的效果上有几个差异点PyCharm 会注入自己的 results collector导致测试输出的 buffer 机制略有不同。某些情况下你在 PyCharm 控制台里看不到用例中的 print 输出即使加了-s。这其实是 PyCharm 调试器的 stdout 重定向特性导致的不是 pytest 的问题。PyCharm 的 Test Runner 面板会额外创建进程或线程去处理事件极端情况下对并发测试比如 pytest-xdist有额外开销。终端里跑 pytest 时颜色和进度条是直接用 ANSI 转义序列渲染的PyCharm 的 Test Runner 里不解析这些转义码所以你看到的是整齐的测试树而不是花花绿绿的点线。结论大批量的回归测试、性能测试、耗时较长的用例建议直接丢给终端跑日常写代码时的快速验证用 PyCharm 的右键或解释器配置跑。没毛病也符合直觉。5. 两种运行方式怎么选从开发调试到 CI 的完整链路5.1 开发期右键运行是最高效的验证方式写代码期间的验证讲究“短平快”我一般直接右键运行单个测试函数。配合断点可以在任意执行位置停下来检查中间变量。这种方式的优点是用例定位零成本缺点是参数控制力弱。想指定某些环境变量右键点不出来。想跳过冒烟标记右键菜单没这个选项。所以开发期我的建议组合是主用右键运行单用例或单类遇到需要传参的时候临时切到 pytest 解释器运行配置把参数填全、跑完、再切回右键模式。两个配置互相配合效率是最高的。5.2 持续集成期终端式 pytest 命令才是王道到了 CI 阶段没有 IDE 可以依赖一切都得靠命令行。这个时候你应该把所有运行参数固化到一个统一入口最简单的方式是在项目根目录放好pytest.ini和conftest.py并在 CI 脚本里只敲一句python -m pytest剩下的交给配置文件去决定。这里有个重点CI 机器上的工作目录和本地项目结构不一定完全一致所以testpaths不能依赖绝对路径要用相对路径。同时不要依赖 PyCharm 里“Working directory”那种隐式设置CI 里的工作目录是显式配置的你必须在脚本层面确保进入项目根目录。5.3 混合场景同一套代码在不同环境跑不同用例实际项目里经常有这种需求开发环境要跑全部用例冒烟测试只跑标记了 smoke 的回归测试只跑标记了 regression 的。用同一个 PyCharm 配置没法自动切换我的做法是配置三个不同的 pytest 解释器运行配置分别命名为“dev all”“smoke”“regression”Parameters 里分别写# dev all python -m pytest # smoke python -m pytest -m smoke # regression python -m pytest -m regression -vPyCharm 支持给每个配置单独设置环境变量所以“dev all”配置里可以挂一个TEST_ENVdevsmoke 配置里挂TEST_ENVstaging。这样你在 PyCharm 下拉框里切换运行配置就等于切换了整套测试环境比在终端里每次敲一堆环境变量舒心得多。6. 高频踩坑记录跑用例时常见的几个疑难杂症6.1 原因不明的“ModuleNotFoundError: No module named xxx”这种错误几乎每个人都有。表面上是“缺依赖”但很多情况下依赖是装了的。真正的原因通常是运行配置里的工作目录不对导致 Python 的sys.path里没有包含项目根目录。pytest 在收集用例前会尝试把 rootdir 插入sys.path取决于是否存在__init__.py及 conftest.py 的位置但如果你把 Working directory 特意改到了某个子目录rootdir 判定就会发生变化。我的排查思路是先在终端里跑python -c import sys; print(sys.path)看看项目根目录在不在路径里。如果不在多半就是 pycharm 配置里工作目录或解释器没有正确设置。另一个通用解法是在项目根目录放一个空的conftest.pypytest 会自动把它所在目录加入sys.pathrootdir 锚定机制很多 import 问题就这样无声无息地解决了。6.2 conftest.py 里的 fixture 失效fixture 是 pytest 的灵魂但 conftest.py 的作用域规则让不少人栽跟头。你要记住一个原则conftest.py 只对该文件所在目录及其所有子目录下的测试可见。如果你在tests/unit/conftest.py里定义了 fixture那tests/integration下的测试是拿不到这个 fixture 的。这不是 PyCharm 的锅而是 pytest 层级作用域的固有逻辑。遇到 fixture 找不到的报错先检查 conftest.py 的位置对不对。如果你想让一个 fixture 全局可用应该放在项目根目录下通常在 rootdir 同一层级的 conftest.py 里而不是某个子目录里。此外要注意 conftest.py 的导入阶段就执行一旦里面存在模块级代码比如读取环境变量缺失时会直接中断整个测试会话比 fixture 本身报错更难排查。6.3 PyCharm 输出里看不到 print 内容这个问题出现过无数次。明明加了-sPyCharm 的 Test Runner 里还是看不到 print 输出。原因PyCharm 对 pytest 的输出捕获有特殊处理。它默认会开启--tbnative和--capturesys的变体逻辑有时候你配置的-s会被覆盖。解决办法是将 PyCharm 的默认 pytest 运行配置里额外加上--captureno或者改用 pytest 解释器运行配置这时参数会真正透传给 pytestprint 就会出现在控制台。如果依然看不到就在用例里用sys.stdout.write()强制写标准输出绕过 pytest 的捕获机制。提示pytest 的-s不等于“完全关闭捕获”。它只是关闭了在用例执行时的捕获但失败信息的展示仍会通过 pytest 的 report 机制加工。真正要让原始输出暴露在控制台必须用--captureno或-s结合--show-capturestdout。6.4 中文乱码和失败断言显示不全pytest 在控制台输出中文断言失败信息时Windows 终端偶尔会乱码macOS 和 Linux 一般没事。乱码的原因多半是终端的编码不是 UTF-8。在代码层面避免问题的方式是断言消息里不要拼一大堆中文而是用简短的英文标识符。比如assert user.name expected_name, fname mismatch: expected{expected_name}, actual{user.name}如果确实需要展示中文在 PyCharm 的 Help → Edit Custom Properties 里加一条file.encodingUTF-8并重启通常能解决。如果你是在终端里跑的命令行 pytestWindows 下可以先执行chcp 65001然后再跑 pytest中文输出就会正常。6.5 pytest-xdist 并行时用例相互干扰并行执行是提升回归效率的利器但用例之间的数据隔离没做好就会互相污染。典型例子多条用例写同一个临时文件或者共用同一个全局状态。用 pytest-xdist 之前先保证每一个用例都是“可以独立重复执行”的。对于测试数据文件建议用 pytest 自带的tmp_pathfixture它会为每条用例分配独立的临时目录对于需要保持全局状态的场景明确使用session级别 fixture并且只放读操作不放写操作。如果并行后出现偶发失败可以先使用pytest -n 0把所有并行退化为串行看问题是否消失。如果串行全过、并行偶败几乎可以断定是用例间存在数据或状态竞争这时候就该用tmp_path重构测试数据管理而不是怀疑 pytest 本身。7. 如何把两种运行方式结合成一套舒服的工作流7.1 日常开发的标准动作我的固定流程是这样写新用例时在 PyCharm 里右键单跑这个函数确认断言逻辑正确涉及多个模块联调时创建一个 pytest 解释器运行配置Parameters 填上-v -s --tbshort把相关模块的测试文件一次性跑完提交代码前切到“all tests”配置用-q模式跑一遍全量确认没有破坏已有功能。这中间有个细节值得分享我给每个运行配置都起了可识别的名字。比如“test_login_single”对应单个测试文件“test_all_quiet”对应全量静默。项目组新同事接手时看配置名就知道当前正常的执行入口是什么省去不少问来问去的沟通成本。7.2 把配置固化到项目文件里共享给队友PyCharm 的 Run/Debug Configuration 默认是保存在.idea/workspace.xml里的这个文件一般会被 git 忽略所以新成员拉下代码后看不到你的配置。想让配置共享可以在.idea/runConfigurations目录下放配置文件但操作繁琐且各版本 PyCharm 格式略有差异。更稳妥的做法把 pytest 的常用参数写在pytest.ini里让所有人不管在 IDE 还是终端跑都是同一套行为再在项目 README 里写清楚运行测试的命令示例。这样一来PyCharm 配置只是你自己本地的个性化入口而项目级的运行规范永远跟着代码仓库走不会因人而异。7.3 我的最终建议先理解 pytest 本身再依赖 PyCharm很多人遇到问题会第一时间怀疑 PyCharm但实际上 PyCharm 只是一个壳它转发的是 pytest 的逻辑。花一晚上把 pytest 的文档翻一遍比琢磨一百个 PyCharm 设置项更管用。你在终端里能把一个用例跑明白、把一条命令的参数说清楚回到 PyCharm 里自然知道哪个设置该动、哪个不该动。最后分享一个小技巧把pytest --version和pytest --help的输出看一遍几秒钟的事但对理解运行逻辑特别有帮助。里面会明确写出 rootdir 的判定规则、已加载插件列表、默认收集规则等关键信息。排查任何疑难杂症时先看一眼这两条命令的输出往往能少绕一大段弯路。
阅读完成 · 觉得有帮助?
咨询建站