ARTICLE DETAIL

资讯详情

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

Spin 运行时测试的自动生成:深入解析 test-codegen-macro 声明式过程宏

Spin 运行时测试的自动生成:深入解析 test-codegen-macro 声明式过程宏 云原生微服务【免费下载链接】spinSpin is the open source developer tool for building and running serverless applications powered by WebAssembly.项目地址https://gitcode.com/gh_mirrors/spin1/spin点击查看免费下载本指南围绕 Spin 仓库中的 test-codegen-macro 声明式过程宏展开讲解它如何基于tests/runtime-tests/tests的目录结构自动生成一批#[test]测试函数从而让开发者在新增一个运行时测试时只需添加测试目录与spin.toml清单而无需手写对应的测试函数。读完本文你将掌握该宏的输入语法ignore列表、生成逻辑snake_case 命名、extern-dependencies-testsfeature 门控、它与运行时测试协议200/500 error.txt的配合方式以及如何在 Spin 的测试套件中扩展新的运行时测试用例。一、背景Spin 运行时测试与测试函数重复编写问题Spin 是一个构建与运行 WebAssembly serverless 应用的开源开发者工具。在其测试体系中有一类专门验证运行时行为的测试即 runtime tests。根据 tests/runtime-tests/README.md 的说明运行时测试旨在验证一个有效的 Spin 清单spin.toml与若干符合 Spin 规范的 WebAssembly 二进制组合起来能够按预期运行或在预期情况下失败它不属于完整端到端集成测试因此不关心 CLI 参数、导致 Spin 无法启动 HTTP 服务器的失败场景等。在 tests/runtime-tests/tests 目录下每个子目录代表一个运行时测试用例如http-no-trailing-slash、internal-http、outbound-mysql、wasi-key-value等每个子目录至少包含一个spin.toml清单可选包含error.txt预期失败时的错误匹配文本和services所需外部服务列表。问题随之而来如果每个测试目录都要在测试代码中手写一个对应的#[test] fn xxx() { ... }那么新增一个运行时测试时就必须同时改两处——添加目录 添加函数二者极易失步。test-codegen-macro 正是为了解决这一重复劳动而存在的。二、宏的核心职责从目录结构生成测试函数README 对宏的定位描述得很精炼A macro for automatically producing#[test]annotated functions based on file directory structure. This is used by the runtime tests so that when adding a runtime test, youre not required to also add a test function corresponding to that runtime test.——即基于文件目录结构自动生成带#[test]注解的函数它被运行时测试使用这样当你新增一个运行时测试时无需再添加与该运行时测试对应的测试函数。该宏是一个声明式过程宏#[proc_macro]入口为 crates/test-codegen-macro/src/lib.rs 中的codegen_runtime_tests/// This macro generates the #[test] functions for the runtime tests. #[proc_macro] pub fn codegen_runtime_tests(input: TokenStream) - TokenStream {它在编译期完成以下工作通过env!(CARGO_MANIFEST_DIR)定位宏所在 crate 的清单目录再拼接../../tests/runtime-tests/tests得到运行时测试目录的绝对路径见 lib.rs。用std::fs::read_dir遍历该目录下每一个子目录非目录项会被跳过见 lib.rs。对每个子目录检查目录下是否存在services文件entry.path().join(services).exists()据此决定是否添加#[cfg(feature extern-dependencies-tests)]属性见 lib.rs检查该测试名是否出现在ignore列表中若是则添加#[ignore]属性将目录名通过to_snake_case()来自heckcrate转换为 Rust 标识符作为测试函数名见 lib.rs生成一个调用run(PathBuf::from(测试目录绝对路径).join(目录名))的#[test]函数。把所有生成的函数拼接成一个TokenStream返回quote::quote!(#(#tests)*)。每个测试目录生成的代码形如#[test] fn outbound_mysql() { run(::std::path::PathBuf::from(/.../tests/runtime-tests/tests).join(outbound-mysql)) }其中run函数由宏的调用方测试模块提供负责真正启动运行时并执行测试详见下文第三节。三、宏的调用方式ignore列表与run回调该宏的唯一公开入口在 tests/runtime.rs这是 Spin 根测试套件cargo test中运行全部运行时测试的模块/// Run the tests found in tests/runtime-tests directory. mod runtime_tests { use std::path::PathBuf; use testing_framework::runtimes::in_process_spin::InProcessSpin; // The macro inspects the tests directory and // generates individual tests for each one. test_codegen_macro::codegen_runtime_tests!( ignore: [ // This test is flaky. Often gets Connection reset by peer errors. // https://github.com/spinframework/spin/issues/2265 outbound-postgres, outbound-postgres-variable-permission ] ); fn run(test_path: PathBuf) { let config runtime_tests::RuntimeTestConfig { test_path, runtime_config: (), on_error: testing_framework::OnTestError::Panic, }; runtime_tests::RuntimeTest::InProcessSpin::bootstrap(config) .expect(failed to bootstrap runtime tests tests) .run(); } ... }从这段真实调用代码可以看出宏的输入契约语法调用形式为codegen_runtime_tests!(ignore: [name1, name2, ...])即一个名为ignore的具名字段值为字符串数组解析约束宏内部的ignores函数lib.rs对输入做了严格校验——成员必须是命名成员且名字必须为ignore表达式必须是字符串数组否则直接panic!(codegen_runtime_tests!() requires ...)。这保证了宏输入的唯一合法形态ignore语义命中列表的测试会带上#[ignore]在默认cargo test中会被跳过可用于标记 flaky 或有外部依赖的用例。上例中outbound-postgres与outbound-postgres-variable-permission就因偶发 Connection reset by peer 而被忽略run约定宏生成的每个#[test] fn xxx()都会调用模块作用域内的run(test_path: PathBuf)。在 tests/runtime.rs 中run通过RuntimeTest::InProcessSpin::bootstrap(config).run()以进程内in-process方式启动一个 Spin 运行时实例并执行该用例错误处理策略为OnTestError::Panic即测试失败直接 panic作为#[test]失败上报。注意宏依赖项heck用于 snake_case 转换、quote生成 Rust 代码、syn解析宏输入见 Cargo.toml[lib]段声明proc-macro true且关闭了 doctest 与单元测试doctest false、test false。四、与运行时测试协议的配合测试用例如何被跑起来宏只负责生成#[test]壳子真正的执行逻辑在runtime-testscrate 中。理解这一点才能明白新增一个测试 新增一个目录的完整含义。4.1 测试目录的约定根据 tests/runtime-tests/README.md一个运行时测试目录必须包含spin.toml清单必选它实际是一个支持插值模板的清单测试运行器支持两类占位符%{source组件名}引用 tests/test-components 中预构建好的 Spin 兼容 WebAssembly 组件例如%{sourcesqlite}会使用名为sqlite的测试组件%{port端口号}引用某个服务暴露的 guest 端口测试运行器会查找暴露该端口的服务并替换为随机分配的主机端口。error.txt可选当该应用预期失败时存在。协议要求测试运行器向/发起 GET 请求组件应返回 200一切正常或 500出错若存在error.txt则应用必须返回 500 且响应体包含error.txt中的文本。services可选列出测试所需的外部服务名一行一个。required_servicessrc/lib.rs读取该文件并交给ServicesConfig启动依赖服务目录下存在services与否正是宏决定是否添加extern-dependencies-testsfeature 门控的依据。4.2 测试执行与通过条件RuntimeTest::runsrc/lib.rs对每个用例发起GET /请求其判定逻辑与 README 完全一致返回200→ 通过返回500且响应体非空 → 读取error.txt若响应体包含其内容则通过否则失败并附带 stderr返回其他状态码或空响应体 → 失败。测试期间copy_manifestsrc/lib.rs会把测试目录的spin.toml模板拷贝到临时目录并完成占位符替换组件路径通过test_components::path解析。因此一个典型的测试目录如 outbound-mysql/spin.toml长这样spin_manifest_version 2 [application] name outbound-mysql authors [Fermyon Engineering engineeringfermyon.com] version 0.1.0 [[trigger.http]] route / component test [component.test] source %{sourceoutbound-mysql} allowed_outbound_hosts [mysql://localhost:%{port3306}] environment { DB_URL mysql://spin:spinlocalhost:%{port3306}/spin_dev }这里%{sourceoutbound-mysql}指向测试组件%{port3306}则由服务编排层解析为实际可用端口——这就是宏背后运行时测试生态的全貌。而同样场景的负向用例 outbound-mysql-no-permission/spin.toml 不声明allowed_outbound_hosts配合目录下的error.txt即可断言权限拒绝行为。4.3 另一种运行方式独立二进制除了通过cargo test走宏生成路径外tests/runtime-tests/src/main.rs 还提供了独立二进制cargo run可接受两个可选参数spin二进制路径与测试目录路径均有默认值并以OnTestError::Log打印日志而非 panic的方式调用RuntimeTest::SpinCli::run_all遍历执行。五、设计价值与扩展思路从实现与调用点可以看出该宏的收益与约束都非常清晰收益单一事实来源。测试目录即测试声明tests/runtime-tests/tests下每新增一个目录只要包含合法spin.toml宏在下次编译时就会自动为其生成#[test]函数彻底消除了目录存在但忘记注册测试函数这类低级遗漏同时ignore列表提供了声明式的跳过机制services探测让需要外部服务的用例自动带上 feature 门控。约束命名映射规则。测试函数名由目录名经to_snake_case()派生因此目录命名应遵循 Snake Case 风格如outbound-mysql→outbound_mysql以避免生成非法或不可读的标识符宏也不支持除ignore外的其他配置项如自定义 feature 名、自定义 run 路径需要扩展时需修改宏本身。适用边界从源码结构可以推断该宏强耦合于CARGO_MANIFEST_DIR向上两级 tests/runtime-tests/tests这个相对布局若移动test-codegen-macrocrate 或运行时测试目录的位置路径需要同步调整它只适合目录结构驱动测试生成这一场景通用性有限但在 Spin 仓库中已被 tests/runtime.rs 稳定使用是理解 Spin 测试分层单元测试 / 运行时测试 / 一致性测试并存的关键一环。六、小结宏codegen_runtime_tests 遍历tests/runtime-tests/tests子目录为每个目录生成调用run(PathBuf)的#[test]函数按需附加#[ignore]与#[cfg(feature extern-dependencies-tests)]输入契约仅支持ignore: [..., ...]具名字段由syn严格解析真实调用见 tests/runtime.rsrun通过RuntimeTest::InProcessSpin引导测试测试协议GET /返回 200 即通过返回 500 且响应体包含error.txt内容即按预期失败详见 tests/runtime-tests/README.md 与 src/lib.rs。对希望为 Spin 贡献新运行时测试的开发者来说工作流被大幅简化在 tests/runtime-tests/tests 下新增目录并写好spin.toml必要时附带error.txt、services宏会在下一次cargo test编译时自动完成测试函数的生成与注册。赞分享云原生微服务【免费下载链接】spinSpin is the open source developer tool for building and running serverless applications powered by WebAssembly.项目地址https://gitcode.com/gh_mirrors/spin1/spin点击查看免费下载相关推荐深入剖析 tests_macros用过程宏为 Rust 项目自动生成文件驱动测试深入剖析 tests_macros用过程宏为 Rust 项目自动生成文件驱动测试 tests_macros 是 Rome面向 JavaScript、Type开发工具Lint格式化前端构建编译器代码质量编辑器ER-Save-Editor宏编程声明宏与过程宏在代码生成中的应用ER Save Editor宏编程声明宏与过程宏在代码生成中的应用 项目背景与宏编程价值 ER Save Editor作为《艾尔登法环》 Elden Ring桌面应用Spin与Wasmtime集成深入了解底层运行时机制Spin与Wasmtime集成深入了解底层运行时机制 想要构建高性能的服务器less应用吗 Spin 作为开源的WebAssembly开发者工具通过深度集云原生微服务上一篇Skia与WebAssembly性能优化内存管理与执行速度下一篇notepad-- 完整指南跨平台文本编辑器的文件对比、批量替换与乱码处理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表