 在本地仿真测试 Workers 存储)
cloudflare-docs Miniflare R2 指南用 r2Buckets 与 getR2Bucket() 在本地仿真测试 Workers 存储【免费下载链接】cloudflare-docsCloudflare’s documentation项目地址: https://gitcode.com/GitHub_Trending/cl/cloudflare-docs本篇基于 cloudflare-docs 仓库中的 Miniflare 存储文档 R2完整讲解如何在 Miniflare 测试环境中声明本地 R2 Bucket、如何在 Worker 内部与 Worker 外部读写 R2 对象以及如何将这套能力嵌入可运行的测试套件。读完后你将能够独立完成声明r2Buckets绑定、通过getR2Bucket()在测试代码中直接 put/get 对象、验证 Worker 与测试代码共享同一份本地存储数据以及用r2Persist控制数据持久化。为什么需要 Miniflare 的 R2 本地仿真Miniflare API 的定位是让开发者在不发起真实 HTTP 请求的情况下向 Worker 派发事件、模拟 Worker 之间的连接并与本地仿真的存储产品交互。根据 Miniflare 入门文档 的说明其本地仿真覆盖了 KV、R2、Durable Objects 等存储产品D1、Cache 亦在 完整选项参考 中列出。这意味着在测试 R2 相关逻辑例如以对象存储实现计数器、缓存层时无需连接任何真实 R2 账户或网络所有env.BUCKET.put()/env.BUCKET.get()调用都作用于一个进程内的本地存储实例测试因此具备确定性、离线性与零成本三个特性。需要特别注意的是一个前提约束Miniflare 不会读取 Wrangler 的配置文件Worker 用到的所有绑定都必须在 Miniflare API 的选项中显式指定见 Writing tests 中的注意事项。因此本地测试 R2 时Bucket 的声明只能出现在 Miniflare 构造选项里而不能依赖wrangler.jsonc中的r2_buckets配置。声明 R2 Bucketsr2Buckets 选项以字符串数组声明为环境添加 R2 Bucket 最直接的方式是通过r2Buckets选项传入字符串数组const mf new Miniflare({ r2Buckets: [BUCKET1, BUCKET2], });数组中的每个名字既是 Bucket 名称同时也是该 Bucket 在 Worker 运行时环境中的绑定名——Worker 内部即可通过env.BUCKET1、env.BUCKET2直接访问对应 Bucket无需任何额外映射。v3 支持的 Record 形式绑定名与 Bucket 名分离从 Miniflare v2 迁移文档 可以看到v3 中kvNamespaces/r2Buckets/d1Databases这些选项除了接受string[]外还接受Recordstring, string形式即“绑定名 - Bucket 名/命名空间 ID”的映射。这一变化的实际收益是多个 Worker 可以以不同的绑定名绑定到同一个 Bucket。这在测试多 Worker 架构例如一个 Worker 写入、另一个 Worker 读取时尤为有用// 示意两个 Worker 以不同绑定名访问同一个本地 Bucket const mf new Miniflare({ workers: [ { name: writer, r2Buckets: { MY_BUCKET: shared-bucket }, // ... }, { name: reader, r2Buckets: { STORE: shared-bucket }, // ... }, ], });无论采用哪种形式Worker 内部的访问方式一致拿到env.绑定名后即可调用与 R2 Workers API 参考 中相同的get/put等接口。在 Worker 外部操作 R2getR2Bucket()对测试而言常常需要在 Worker 之外直接往 R2 存储里写入或读取数据——例如预置初始状态、断言 Worker 处理后的存储结果。Miniflare 提供了getR2Bucket方法来完成这件事方法本身在 Miniflare 参考 中列出。下面完整继承自 R2 文档 的示例演示了这一点一个 Worker 读取count对象、加 1 后写回测试代码先预置count为1再向 Worker 发请求两边共享同一份本地存储import { Miniflare } from miniflare; const mf new Miniflare({ modules: true, script: export default { async fetch(request, env, ctx) { const object await env.BUCKET.get(count); const value parseInt(await object.text()) 1; await env.BUCKET.put(count, value.toString()); return new Response(value.toString()); } } , r2Buckets: [BUCKET], }); const bucket await mf.getR2Bucket(BUCKET); await bucket.put(count, 1); const res await mf.dispatchFetch(http://localhost:8787/); console.log(await res.text()); // 2 console.log(await (await bucket.get(count)).text()); // 2逐步拆解该示例的执行链r2Buckets: [BUCKET]声明本地 BucketWorker 内对应env.BUCKETmf.getR2Bucket(BUCKET)在测试进程中拿到同一 Bucket 的句柄。从文档示例的结构看与 KV 的 getKVNamespace 示例 完全平行外部句柄与 Worker 内env.BUCKET指向同一存储这正是测试断言的基础bucket.put(count, 1)预置初始值——此时还没有任何请求到达 Workermf.dispatchFetch(http://localhost:8787/)向本地 HTTP 服务器派发 fetch 事件。Miniflare 默认监听 8787 端口见 完整选项参考 中port: 8787的默认值注释派发后 Worker 读到1、加 1 写回2并响应2最后通过外部句柄再读一次count确认持久化的结果同样是2完成“Worker 写、测试读”的闭环验证。getR2Bucket()返回的句柄在 API 层面与 Worker 内的 R2 绑定一致put(key, value)写入字符串值get(key)返回对象后需再调用.text()或.arrayBuffer()等解出内容这与 R2 Workers API 的对象读取语义相同。此外Miniflare 还提供了更通用的getBindings()方法可一次性拿到环境中所有绑定KV/R2 命名空间、变量等的句柄。Writing tests 文档 展示的就是这种用法const bindings await worker.getBindings(); // bindings.BUCKET 即与 env.BUCKET 相同的 R2 绑定如果你倾向通过getBindings()而非getR2Bucket()访问存储两种方式在 R2 场景下是等价的可按测试框架的组织习惯选用。R2 数据持久化r2Persist 选项与 Miniflare 中 Durable ObjectsdurableObjectsPersist默认.mf/do见 Durable Objects 文档和 CachecachePersist默认./.mf/cache见 Cache 文档的平行设计相类似R2 的持久化由r2Persist选项控制。在 Miniflare 完整选项参考 中可以看到该选项的注释const mf new Miniflare({ r2Buckets: [BUCKET], // R2 bucket to bind r2Persist: ./r2-data, // Persist R2 data (to optional path) });r2Persist接受一个目录路径用于将 R2 数据持久化到文件系统的指定位置。从与 Durable Objects、Cache 两个兄弟文档的对照看不设置该选项时数据在内存中维护可以在同一Miniflare实例的 reload 之间保留但不跨实例而需要跨进程或跨测试会话保留对象数据例如调试期间反复重启测试进程仍希望看到之前写入的对象时才应启用r2Persist并指定路径。嵌入测试套件一个完整的可运行形态将上述能力放入测试框架结构上与 Writing tests 文档 的node:test范式一致before中构造并等待ready测试用例中派发请求或操作绑定after中dispose()清理。一个针对 R2 计数逻辑的完整测试骨架如下示例脚本内容与 R2 文档 完全一致import assert from node:assert; import test, { after, before } from node:test; import { Miniflare } from miniflare; test(r2 counter worker, async () { const mf new Miniflare({ modules: true, script: export default { async fetch(request, env, ctx) { const object await env.BUCKET.get(count); const value parseInt(await object.text()) 1; await env.BUCKET.put(count, value.toString()); return new Response(value.toString()); } } , r2Buckets: [BUCKET], }); await mf.ready; try { // 测试侧预置初始状态 const bucket await mf.getR2Bucket(BUCKET); await bucket.put(count, 1); // 通过本地 HTTP 派发 fetch 事件 const res await mf.dispatchFetch(http://localhost:8787/); assert.strictEqual(await res.text(), 2); // 断言 Worker 写回后的存储状态 assert.strictEqual(await (await bucket.get(count)).text(), 2); } finally { await mf.dispose(); } });该骨架的关键点modules: true与script字符串用于在测试内联定义 Worker无需落盘脚本文件若 Worker 是实际项目文件改用scriptPath或modules模块图方式见 Writing tests 中“More complex Workers”await mf.ready等待本地 HTTP 服务器就绪后再发起请求入门文档 明确建议等待ready属性dispose()用于断开存储数据库连接、停止监听测试收尾时不可省略测试代码运行在 Node.js 进程中只有 Worker 本身运行在workerd运行时内见 Writing tests 的运行时说明因此“测试侧读写 R2”走的是 Miniflare 暴露的绑定句柄而不是 Worker 内部代码路径。与同类存储文档的关系及延伸阅读Miniflare 的 R2 文档与同目录下的 KV、Durable Objects、D1、Cache 文档采用完全一致的叙述结构“声明绑定 - Worker 内访问 - 通过外部方法操作getKVNamespace/getDurableObjectNamespace/getD1Database/getCaches”。R2 对应的三方即r2Buckets声明、env.BUCKET内访问、getR2Bucket()外操作。掌握其中任一篇的套路其余存储类型的测试写法可直接迁移。其余参考入口Miniflare 入门与完整选项参考r2Buckets/r2Persist的完整选项注释、dispatchFetch与getWorker事件派发、setOptions/dispose生命周期Writing testsMiniflare 在node:test中的完整集成范式以及“Miniflare 不读取 Wrangler 配置”的前提约束Miniflare v2 - v3 迁移r2Buckets等选项接受Recordstring, string的变更说明R2 Workers API 参考Worker 内env.BUCKET绑定的完整 R2 接口get/put/list等本地仿真遵循相同的 API 语义。小结Miniflare 的 R2 仿真由三个要素构成r2Buckets选项声明本地 Bucket 并自动形成env绑定Worker 内部按标准 R2 Workers API 读写对象测试代码通过getR2Bucket()或getBindings()拿到与 Worker 共享的存储句柄完成预置状态与结果断言。配合r2Persist控制跨会话持久化、await mf.ready与dispose()管理生命周期即可在完全离线的环境中构建确定性的 R2 存储集成测试。【免费下载链接】cloudflare-docsCloudflare’s documentation项目地址: https://gitcode.com/GitHub_Trending/cl/cloudflare-docs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考