yfinance 单元测试指南:用 pytest 验证行情下载与价格修复功能
2026/9/11 16:11:49 网站建设 项目流程

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.4Sphinx 模板依赖
pydata-sphinx-theme==0.15.4文档主题

此外还有一个可选的repair依赖组(scipy>=1.6.3scikit-learn>=1.0),它服务于价格修复(price repair)功能——如果你要运行 test_price_repair.py 中涉及机器学习/科学计算的用例,建议一并安装:

pip install -e ".[dev,repair]"

建议在独立的虚拟环境中执行上述安装,避免污染系统级 Python 环境。

二、运行测试的四种方式

文档给出了从“全部测试”到“单个测试方法”的完整运行粒度,实际使用频率很高,逐条整理如下。

1. 运行全部测试

pytest

pytest 默认按 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_types

test_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.pyclassmethod逐级限定执行范围。三个位置可任意省略以扩大匹配范围,例如:

  • 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_gbl

context.py 承担了三件事:

  1. 路径注入:把仓库根目录插入sys.path,保证测试导入的是本地源码而不是site-packages里的安装包;
  2. 时区缓存隔离:调用yfinance.set_tz_cache_location()把时区缓存放到平台用户缓存目录下的py-yfinance-testing子目录,并在缓存目录“早于今天”时自动删除重建,避免测试之间互相污染;
  3. 全局会话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_dailytest_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.py102Ticker 对象全量 API
tests/test_prices.py27行情历史、下载、时区、无效代码
tests/test_utils.py25工具函数
tests/test_auth.py20认证流程
tests/test_price_repair.py15价格修复
tests/test_data.py15基本面数据
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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询