ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

OpenMed 测试套件深度指南:基于 pytest 的离线优先单元测试与集成测试实践

OpenMed 测试套件深度指南:基于 pytest 的离线优先单元测试与集成测试实践 OpenMed 测试套件深度指南基于 pytest 的离线优先单元测试与集成测试实践【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed本指南以 OpenMed 仓库的 tests/README.md 为骨架系统讲解其测试目录布局、pytest 标记体系、共享 fixture 设计、从源码安装依赖的方法以及全部常用运行命令。OpenMed 是一个本地优先local-first的医疗健康 AI SDK其测试套件的核心设计目标是在核心依赖安装完成后完全离线即可执行全部测试——所有下游 Hugging Face 模型 API 均被 mock 替换。读完本文你将掌握 OpenMed 测试套件的完整结构、如何复现测试环境、如何分层运行单元/集成/慢速/模糊测试以及如何利用仓库中的共享 fixture 编写自己的新测试。测试套件总览为离线执行而设计的目录结构OpenMed 的tests/目录承载了单元测试与集成测试的完整覆盖。整个套件依赖pytest并通过大量 mock 屏蔽下游 Hugging Face API因此只要核心依赖安装完毕就可以在网络隔离的环境下运行。这一点与 OpenMed数据不出网、全程本地推理的产品定位一脉相承——测试环境同样不依赖外部网络。官方文档定义的核心布局如下unit/—— 快速、隔离的单元测试覆盖配置管理、模型加载辅助函数、分词tokenisation、格式化与各类工具模块integration/—— 更高级别的场景测试通过 mock 的 transformers pipeline 演练公开 API 表面例如analyze_text、list_modelsfixtures/—— 共享的样例文本与可复用的 pytest fixtureconftest.py—— 全局 fixture负责 mock transformers 组件、配置重置与样例数据。从仓库实际内容看套件规模远超这四个目录tests/unit下有core、cli、clinical、interop、eval、multimodal、risk、service、traces、training等数十个分类目录累计超过千个测试文件此外还有fuzz/基于 Hypothesis 的属性测试与语料回放、property/流水线阶段契约测试、browser/Playwright 端到端测试、web/、mobile/Flutter FFI 与 React Native 桥接对比以及desktop/Tauri 客户端。tests/run-tests.sh与根目录 pyproject.toml 中的 pytest 配置把这些测试统一编排进 CI。从源码安装测试依赖官方文档给出的安装流程是创建全新的虚拟环境以可编辑editable模式安装包并附带测试依赖。测试期间 transformer 层会被 patch但必须仍然可被 import因此需要安装transformerstorch可选。# 从仓库根目录执行 git clone https://gitcode.com/GitHub_Trending/ope/openmed.git cd openmed python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -e . pip install pytest pytest-cov transformers # 可选为 transformers 安装 CPU 后端 pip install torch --index-url https://download.pytorch.org/whl/cpu如果要在自定义测试中访问 Hugging Face 的受限gated模型运行 pytest 前需要导出HF_TOKEN而仓库自带的测试不需要任何网络访问。一个更省事的替代方案是直接使用dev可选依赖组。根目录 pyproject.toml 中的[project.optional-dependencies].dev已经聚合了pytest7.0、pytest-cov4.0、pytest-timeout2.3、hypothesis6.100、ruff0.15.22、huggingface-hub0.30等全套开发工具tests/run-tests.sh正是这样做的pip install -e .[dev]从源码结构看OpenMed 还提供了丰富的运行时可选依赖mlx、onnx、torch、hf、gliner等见 pyproject.toml测试时应按被测模块按需安装例如涉及 ONNX 推理的测试需要pip install -e .[onnx]。运行测试从全量到分层在项目根目录执行pytest即可运行完整的单元 集成套件pytest只运行轻量的单元测试pytest tests/unit # 或者通过 marker 过滤 pytest -m not integration只运行集成场景pytest -m integration生成覆盖率报告pytest --covopenmed --cov-reportterm-missing这些命令能够成立依赖 pyproject.toml 中的[tool.pytest.ini_options]配置markers [ integration: marks end-to-end or external integration tests, slow: marks tests that are expected to run slowly, contract: marks property-based stage-boundary contract tests, fuzz: marks property-based (Hypothesis) fuzz tests, doctest_examples: runs doctest examples for targeted public modules ] python_files [test_*.py, *_test.py] testpaths [tests]也就是说pytest 会从tests目录收集所有test_*.py/*_test.py文件并注册了五类 markerintegration端到端或外部集成、slow预期耗时较长的测试、contract基于属性的流水线阶段边界契约测试、fuzz基于 Hypothesis 的模糊测试与doctest_examples针对指定公共模块的 doctest。例如tests/unit/core/test_result_cache.py与tests/unit/core/test_pipeline_latency.py中就同时使用了slow/integration等标记组合。仓库还提供了编排好的脚本 tests/run-tests.sh它模拟 CI 的完整流程#!/usr/bin/env bash set -euo pipefail python3 -m venv .venv source .venv/bin/activate python -m pip install --upgrade pip /tmp/pip-up.log pip install -e .[dev] /tmp/pip-install.log ruff check . ruff format --check . # 不包含慢速标记的核心测试套件为 zero-shot 模块收集覆盖率 pytest -m not slow --covopenmed/ner --cov-reportterm-missing # zero-shot 慢速检查依赖缺失时优雅跳过 pytest -m slow脚本展示了三个关键实践先做代码规范检查ruff再跑测试核心套件用-m not slow排除慢速测试同时单独为openmed/ner模块统计覆盖率慢速测试单独收尾并在依赖缺失时优雅跳过。所有测试之所以被设计为可离线运行正是因为广泛使用了 mock——官方文档明确指出失败要么指向 OpenMed 代码本身的回归要么指向本地缺失的依赖而不是网络问题。conftest.py 拆解全局 mock 与状态隔离测试套件的离线能力集中体现在 tests/conftest.py。它以unittest.mock构造了一整套模拟的 transformers 组件并用三个autousefixture 保证每个测试之间的全局状态相互隔离。数据类 fixturefixture类型说明sample_configOpenMedConfig构造OpenMedConfig(default_orgTestOrg, cache_dir/tmp/test_cache, devicecpu, log_levelDEBUG, timeout60)用于配置相关测试sample_textstr短医疗文本Patient John Doe has diabetes and hypertension. Prescribed metformin 500mg daily.sample_long_textstr较长的多段临床文书主诉、现病史、体格检查、实验室结果、评估与计划覆盖长文本与多句子场景sample_predictionslist[dict]三条模拟模型预测B-CONDITIONdiabetes, 0.95、B-MEDICATIONmetformin, 0.89、B-DOSAGE500mg, 0.87模拟组件 fixturemock_tokenizer一个Mock分词器tokenize返回[patient, has, diabetes]并提供convert_ids_to_tokens、convert_tokens_to_string其调用返回值是一个自定义MockEncoding实现了word_ids()返回[None, 0, 1, 2, None]并携带input_ids、attention_mask、offset_mapping、special_tokens_mask等标准编码字段——这正是 transformers fast tokenizer 的典型返回结构。mock_model模拟 token-classification 模型配置num_labels3、problem_typetoken_classification、architectures[BertForTokenClassification]。mock_pipeline模拟 HuggingFace pipeline 调用返回两条实体预测B-CONDITION/diabetes、B-MEDICATION/metformin含 score、word、start、end 字段。mock_model_info模拟 Hub 模型元信息modelId、author、downloads、likes、library_name、tags、pipeline_tag用于模型注册表 / 搜索相关测试。自动执行的隔离 fixturepytest.fixture(autouseTrue) def reset_config(): yield openmed.core.config.set_config(OpenMedConfig()) pytest.fixture(autouseTrue) def reset_tokenizer_cache(): clear_tokenizer_cache() yield clear_tokenizer_cache() pytest.fixture(autouseTrue) def clear_paged_kv_service_env(monkeypatch): for env_var in _PAGED_KV_SERVICE_ENV_VARS: monkeypatch.delenv(env_var, raisingFalse)三个autousefixture 分别解决三类全局状态污染问题全局配置重置openmed.core.config维护一个进程级单例_config见 openmed/core/config.py 的get_config/set_config若测试之间不重置前一个测试写入的配置会泄漏到后续测试。reset_config在每次测试结束后恢复为默认OpenMedConfig()。进程级分词器缓存清空OpenMed 在 openmed/processing/tokenizer_cache.py 中实现了一个容量 32、线程安全RLock的 LRU 分词器缓存。reset_tokenizer_cache在测试前后各调用一次clear_tokenizer_cache()确保 mock 的分词器与真实缓存互不干扰。MLX paged KV-cache 环境变量清理_PAGED_KV_SERVICE_ENV_VARS列出的五个OPENMED_SERVICE_MLX_PAGED_KV_CACHE_*环境变量会在测试间被monkeypatch.delenv移除避免可选的 MLX paged KV-cache 服务配置泄漏进无关测试——这也印证了 OpenMed 的 MLX 推理路径是可选的、环境驱动的。TestHelpers面向新测试的构造工具conftest.py末尾的TestHelpers类同时以test_helpersfixture 暴露提供两个静态方法create_entity_prediction(text, label, confidence, startNone, endNone)基于openmed.processing.outputs.EntityPrediction构造单条实体预测create_prediction_result(text, entities, model_nametest-model)把[{word, entity, score, ...}]形式的预测列表转换为完整的PredictionResult含timestamp与processing_time。这使新测试可以直接复用统一的预测数据模型不必重复手工构造。集成测试如何驱动真实公共 APItest_end_to_end.py 展示了集成层mock 底层、驱动真实 API的写法。以test_analyze_text_full_pipeline为例它依次 patch 了openmed.core.backends._module_available、openmed.core.models.HF_AVAILABLE、pipeline、AutoConfig、AutoTokenizer、AutoModelForTokenClassification然后调用公开入口analyze_text(sample_text, model_namemedical-ner, config...)断言返回结果具备text、entities、model_name属性且实体数量正确。analyze_text的真实实现位于 openmed/init.py其完整调用链为validate_input→validate_model_name→ModelLoader.create_pipeline内部使用AutoConfig/AutoTokenizer/AutoModelForTokenClassification组装token-classificationpipeline→ 可选句子切分与分块chunk→ner_pipeline(inference_input)→ 边界修正与可选 medical-tokenizer 重映射 →format_predictions输出。集成测试通过 mockAutoConfig/AutoTokenizer/AutoModelForTokenClassification拦截了这条链的模型加载环节从而在完全无网环境下验证了analyze_text的参数解析、pipeline 组装与结果格式化逻辑。同文件中的其他测试还覆盖了list_modelsmockget_all_models后断言返回模型 id 列表、包结构__all__导出完整性、配置流get_config/set_config往返、文本处理管线TextProcessor的clean_text/segment_sentences/extract_medical_entities、输出格式化dict/json/html 三种格式与错误处理空输入与非法模型名校验抛ValueError。标记为pytest.mark.slow的性能类测试则验证长文本处理耗时小于 1 秒。共享样例数据fixtures 目录fixtures/sample_medical_texts.py 提供了更丰富的真实感医疗语料CLINICAL_NOTE_1/CLINICAL_NOTE_2完整的结构化临床文书主诉、既往史、用药、体格检查、评估与计划MEDICATION_LIST_1/MEDICATION_LIST_2用药列表含剂量、频次与给药时间SHORT_TEXTS8 条短句诊断、用药、血压、过敏史、手术史等PROCEDURE_NOTE/RADIOLOGY_REPORT操作记录与放射报告TEST_CASES带期望实体CONDITION/MEDICATION/DOSAGE/VITAL_SIGN的标注用例EDGE_CASES空串、纯空白、超长文本、emoji、医学缩写H/O DM, HTN, CAD、处方格式Metformin 500 mg BID x 30 days #30 disp等边界输入BATCH_TEST_DATA批量处理模拟数据。这些数据覆盖了实体抽取、剂量解析、生命体征识别、缩写消歧、批处理与边界行为等多个测试面是单元测试复用的主要语料来源。除此之外tests/fixtures下还有按领域组织的海量 JSON/JSONL 黄金数据如clinical/、pii/、i18n/、fhir/、interop/omop/、risk/等支撑临床关系抽取、多语言 PII、FHIR/OMOP 互操作、差分隐私预算等专项测试。编写新测试的推荐实践综合仓库的测试组织方式为 OpenMed 贡献新测试时可以参考以下模式放到正确的层级纯逻辑、无模型依赖的测试放tests/unit/对应模块子目录演练analyze_text、deidentify、list_models等公共 API 的场景放tests/integration/耗时较长或需要真实资源如真实句子切分器、容器、模型下载的测试追加pytest.mark.integration或pytest.mark.slow属性化输入用pytest.mark.fuzz Hypothesis。优先复用 conftest fixture医疗文本直接用sample_text/sample_long_text/sample_predictions需要预测对象用test_helpers.create_prediction_result配置相关的测试用sample_config并通过set_config设置、依赖reset_config自动还原。mock 掉模型与 Hub参照test_end_to_end.py用patch拦截AutoConfig/AutoTokenizer/AutoModelForTokenClassification/pipeline或直接使用mock_pipeline/mock_tokenizerfixture涉及模型元信息用mock_model_info。注意进程级状态测试不应假设全局配置与分词器缓存是干净的——autouse fixture 已保证隔离但自建的全局状态也应遵循用完即清的对称模式。排障速查现象可能原因处理方式测试因ImportError: HuggingFace transformers is required...失败未安装transformers即便测试会 mock 它也必须可 importpip install transformers或pip install -e .[hf]集成测试尝试联网缺少HF_TOKEN或测试误触真实 Hub仓库自带测试全部离线自定义测试如需 gated 模型先export HF_TOKEN...慢速测试拖慢全量运行误把slow测试纳入常规 CI用pytest -m not slow过滤覆盖率统计缺失某模块--cov未覆盖目标包参考run-tests.shpytest --covopenmed/ner --cov-reportterm-missing测试间相互污染进程级配置 / tokenizer 缓存 / 环境变量泄漏依赖conftest.py的 autouse fixture新全局状态遵循对称清理结语OpenMed 的测试套件是一个围绕离线可复现精心设计的体系tests/目录分层清晰、conftest.py 用一套 mock 组件与三个自动清理 fixture 保证了测试的确定性与隔离性pyproject.toml 中的 marker 体系让单元、集成、慢速、契约与模糊测试可以按需组合run-tests.sh 则把 lint、覆盖率与分层测试编排成了可一键复现的 CI 流程。无论是复现回归、评估覆盖率还是为 OpenMed 贡献新测试本文的命令与 fixture 速查都能让你快速上手。【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表