yfinance 单元测试指南:用 pytest 验证行情下载与价格修复功能
【免费下载链接】yfinanceDownload market data from Yahoo! Finance's API项目地址: https://gitcode.com/GitHub_Trending/yf/yfinance
yfinance 是下载 Yahoo! Finance 行情数据的开源 Python 库,其行情获取、价格修复、时区处理等核心逻辑都依赖一套自动化测试来守护。本文以仓库文档 testing.rst 为骨架,系统讲解如何在本地安装开发依赖、以四种方式运行 pytest 测试、理解测试套件的组织方式,并结合 pyproject.toml 与tests/目录下的源码证据,帮助你快速上手为 yfinance 贡献代码或排查回归问题。
一、准备工作:安装开发依赖
yfinance 的测试基于pytest编写。文档明确要求:如果你还没有安装开发依赖,请先执行以下命令:
pip install -e ".[dev]"这条命令包含两个关键信息,可以从 pyproject.toml 中得到印证:
-e(editable)表示以可编辑模式安装,本地源码的改动会即时生效,适合开发调试场景;[dev]是项目声明的可选依赖组(extra),位于[project.optional-dependencies]段,包含:
| 依赖 | 版本约束 | 用途 |
|---|---|---|
| pytest | >=9.0.3 | 测试框架 |
| pytest-cov | >=7.1.0 | 覆盖率统计 |
| ruff | >=0.15.16 | 代码风格与静态检查 |
| sphinx | ==8.0.2 | 构建文档 |
| sphinx-copybutton | ==0.5.2 | 文档复制按钮 |
| jinja2 | ==3.1.4 | Sphinx 模板依赖 |
| pydata-sphinx-theme | ==0.15.4 | 文档主题 |
此外还有一个可选的repair依赖组(scipy>=1.6.3、scikit-learn>=1.0),它服务于价格修复(price repair)功能——如果你要运行 test_price_repair.py 中涉及机器学习/科学计算的用例,建议一并安装:
pip install -e ".[dev,repair]"建议在独立的虚拟环境中执行上述安装,避免污染系统级 Python 环境。
二、运行测试的四种方式
文档给出了从“全部测试”到“单个测试方法”的完整运行粒度,实际使用频率很高,逐条整理如下。
1. 运行全部测试
pytestpytest 默认按 pyproject.toml 中[tool.pytest.ini_options]的testpaths = ["tests"]配置,自动发现并执行tests/目录下的所有测试。
2. 运行某个测试文件
pytest tests/test_prices.py该命令只执行tests/test_prices.py中的用例。例如TestPriceHistory类会验证日线数据索引是否为 0 点时间、yf.download()多标的下载是否返回正确的多级列等(见 test_prices.py)。
3. 运行单个测试
文档中的示例为:
pytest tests/test_prices_repair.py::TestPriceRepair::test_ticker_missing这里有一个文档与代码不完全同步的细节值得注意:当前仓库中该文件的真实名称是tests/test_price_repair.py(无s),且TestPriceRepair类中并没有名为test_ticker_missing的方法——文档示例可能滞后于代码演进。基于当前代码,等效且真实可运行的命令是:
pytest tests/test_price_repair.py::TestPriceRepair::test_typestest_types会验证Ticker.history(repair=True)的返回类型是否为pandas.DataFrame、数据非空,并调用_lazy_load_price_history()._reconstruct_intervals_batch()重建周线区间(见 test_price_repair.py)。
4. 通用命令模板
pytest tests/{file}.py::{class}::{method}这是 pytest 标准的“节点选择”(node selection)语法,file.py、class、method逐级限定执行范围。三个位置可任意省略以扩大匹配范围,例如:
pytest tests/test_price_repair.py::TestPriceRepair只跑该类下的所有用例;pytest tests/test_price_repair.py::TestPriceRepairAssumptions::test_resampling只跑该用例。
如果想了解更多参数,可参考 pytest 官方文档(原文档的seealso也指向 pytest 文档,本文不赘述链接)。
三、测试套件结构:源码级解析
理解了运行方式后,再看仓库tests/目录的组织方式,能更清楚每个命令背后实际在测什么。
3.1 目录构成
tests/ ├── __init__.py ├── context.py # 测试公共环境:路径注入、时区缓存、会话 ├── data/ # 固定的行情 CSV 数据(bad / fixed 成对) ├── test_prices.py # 行情历史核心用例 ├── test_price_repair.py# 价格修复用例(100x 错误、坏拆股、坏股息等) ├── test_ticker.py # Ticker 对象 API 用例(用例数量最多的文件) ├── test_utils.py # 工具函数 ├── test_auth.py # 认证(Cookie/CRUMB) ├── test_data.py # 基本面/估值数据 ├── test_lookup.py / test_search.py / test_market.py ... └── test_cache.py / test_cache_noperms.py # 缓存行为3.2 context.py:测试环境的公共入口
每个测试文件的第一行通常都是:
from tests.context import yfinance as yf from tests.context import session_gblcontext.py 承担了三件事:
- 路径注入:把仓库根目录插入
sys.path,保证测试导入的是本地源码而不是site-packages里的安装包; - 时区缓存隔离:调用
yfinance.set_tz_cache_location()把时区缓存放到平台用户缓存目录下的py-yfinance-testing子目录,并在缓存目录“早于今天”时自动删除重建,避免测试之间互相污染; - 全局会话:
session_gbl目前为None(文件内保留了基于requests_cache/requests_ratelimiter的限速缓存会话实现,但已注释,原因是项目切换到curl_cffi后不再适用)。
3.3 测试类的组织方式
虽然运行器是 pytest,但用例本身大量使用unittest.TestCase风格,并通过setUpClass/tearDownClass统一创建和关闭会话。例如TestPriceRepair(见 test_price_repair.py):
class TestPriceRepair(unittest.TestCase): session = None @classmethod def setUpClass(cls): cls.session = session_gbl cls.dp = os.path.dirname(__file__) @classmethod def tearDownClass(cls): if cls.session is not None: cls.session.close()这种混合风格的好处是:pytest 完全兼容unittest.TestCase,同时setUpClass只初始化一次,能显著减少大批量行情请求带来的网络开销。
3.4 数据驱动的固定样本
tests/data/下存放了大量成对的 CSV 样本,命名规律是{ticker}-{interval}-{场景}.csv与{ticker}-{interval}-{场景}-fixed.csv(错误样本 vs 修复后正确样本)。例如:
AET-L-1d-100x-error.csv/AET-L-1d-100x-error-fixed.csv:100 倍单位错误;4063-T-1d-bad-stock-split.csv/4063-T-1d-bad-stock-split-fixed.csv:错误拆股;1398-HK-1d-bad-div.csv/1398-HK-1d-bad-div-fixed.csv:错误股息;DODFX-1d-cg-double-count.csv/DODFX-1d-cg-double-count-fixed.csv:资本利得重复计算。
对应的测试(如test_repair_100x_block_daily、test_repair_bad_stock_splits)会读取这些 CSV,调用价格修复内部方法(_fix_unit_switch、_fix_bad_stock_splits、_fix_bad_div_adjust等),再用np.isclose与 fixed 版本比对,并校验"Repaired?"列的存在性。这套固定样本让价格修复逻辑可以在不依赖网络的前提下反复回归。
3.5 各测试文件的规模与主题
从源码统计,各文件以def test_开头的测试方法数量大致如下:
| 测试文件 | 测试方法数(约) | 覆盖主题 |
|---|---|---|
| tests/test_ticker.py | 102 | Ticker 对象全量 API |
| tests/test_prices.py | 27 | 行情历史、下载、时区、无效代码 |
| tests/test_utils.py | 25 | 工具函数 |
| tests/test_auth.py | 20 | 认证流程 |
| tests/test_price_repair.py | 15 | 价格修复 |
| tests/test_data.py | 15 | 基本面数据 |
| tests/test_lookup.py / test_market.py 等 | 若干 | 查询、市场、区域数据 |
需要说明的是,这些用例大多会发起真实网络请求(通过session访问 Yahoo! Finance),因此测试结果受网络环境、Yahoo 接口变动与行情数据变化影响较大。
四、当前测试状态与排错实战
原文档专门加了一条note,如实说明了一个重要现状:
The tests are currently failing already(测试当前本就处于失败状态) 标准结果:Failures: 11,Errors: 93,Skipped: 1
也就是说,在文档编写时的基线状态下,直接运行pytest会看到 11 个失败、93 个错误、1 个跳过。这通常不是代码本身的问题,而是因为:
- 大部分用例依赖 Yahoo! Finance 的实时接口,接口变动、限流或字段调整都会导致断言失败;
- 部分用例依赖固定的网络返回快照,数据漂移后难以复现;
- 个别用例被显式跳过(
skipTest),例如test_repair_gbp_not_converted在取不到XDEV.L数据时跳过(见 test_price_repair.py)。
调试建议
- 先看全貌:
pytest -q只输出汇总,快速判断失败规模; - 定位首个失败即停:
pytest -x,遇到第一个失败立刻停止,避免浪费时间; - 按关键字过滤:
pytest -k "repair",只跑名称含repair的用例; - 只看详细输出:
pytest -v --tb=short,显示每个用例的完整名称与简洁回溯; - 分组验证:先跑不依赖网络的用例(如读取
tests/data/固定 CSV 的价格修复用例),确认本地环境无问题后,再跑网络类用例; - 善用
-s:测试代码中大量使用print()输出调试信息(如test_price_repair.py中失败时打印df_truth/df_bad/dfr),用pytest -s可以看到这些中间结果。
修正文档中的示例
如前所述,文档第 3 节的示例命令pytest tests/test_prices_repair.py::TestPriceRepair::test_ticker_missing在当前仓库中不可直接执行。请以仓库实际文件为准,例如:
pytest tests/test_price_repair.py::TestPriceRepair::test_types这类“文档与代码脱节”的情况在活跃开发的项目中很常见,阅读时以源码为最终依据即可。
五、配套的开发工具链
测试只是开发流程的一环。yfinance 的文档把开发工作分成四个部分(见 development/index.rst):代码贡献(code.rst,含 dev/main 双分支模型)、分支运行(running.rst)、文档编写(documentation.rst)与测试(testing.rst)。
与本主题相关的配套工具包括:
- ruff:代码风格检查,配置见 ruff.toml。规则集刻意固定(
E4/E7/E9/F),并忽略E702,以保证 CI 在不同 ruff 版本下结果一致; - pytest-cov:已列入 dev 依赖,可用
pytest --cov=yfinance --cov-report=term-missing查看各模块覆盖率; - CI 确定性:
pyproject.toml中[tool.pytest.ini_options]只设置了testpaths = ["tests"],保持最小化配置,具体运行参数交给命令行。
六、小结
围绕 testing.rst,本文覆盖了从依赖安装(pip install -e ".[dev]")到四种 pytest 运行方式(全部 / 单文件 / 单用例 /file::class::method模板)的完整流程,并结合仓库源码剖析了tests/目录的结构、context.py的环境注入机制、tests/data/固定样本的作用,以及当前“11 失败 / 93 错误 / 1 跳过”的测试基线状态。
对想参与 yfinance 开发的读者,建议按此顺序操作:克隆并安装 dev 依赖 → 运行pytest -x观察基线 → 用-k关键字过滤出与改动相关的用例 → 利用tests/data/固定样本离线验证。这样既能快速建立信心,也能在真实网络环境下区分“环境差异”与“真实回归”,为后续提交修复或新功能打下可靠的验证基础。
【免费下载链接】yfinanceDownload market data from Yahoo! Finance's API项目地址: https://gitcode.com/GitHub_Trending/yf/yfinance
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考