RxDB 测试实战指南:内存存储、using 资源管理与多标签页模拟
2026/9/20 20:28:32 网站建设 项目流程
  • 数据库
  • NoSQL
  • 嵌入式数据库
  • 实时数据库

【免费下载链接】rxdb

The local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/

项目地址:https://gitcode.com/gh_mirrors/rx/rxdb
点击查看免费下载

为 RxDB 应用编写测试是保障可靠性的关键一环。RxDB 运行在众多不同的环境中(浏览器、Node.js、React Native、Electron 等),测试策略会随环境而变,但存在一些通用模式能让测试更快、更稳、更易维护。本文以仓库文档 testing.md 为主体,结合 RxDB 源码与仓库内真实测试用例,系统讲解四类核心实践:使用memoryRxStorage 编写单元测试、利用 TypeScript 的using关键字自动释放资源、用dbCount()校验测试清理、以及在同一进程内模拟多标签页(Multi-Tab)行为。读完本文,你将掌握一套可直接复制的 RxDB 测试脚手架与底层原理。

为什么 RxDB 测试需要"通用模式"

RxDB 的架构决定了它的测试面比较广:同一套 API 要在浏览器 IndexedDB、Node.js 文件系统、React Native AsyncStorage、Electron 等多个运行时上保持一致行为。因此官方仓库在 test/ 目录下维护了覆盖全部核心模块的测试套件——从rx-database.test.tsrx-collection.test.tsrx-query.test.tsreplication-*.test.tsrx-storage-*.test.ts,并通过 test/unit.test.ts、test/unit/full.node.ts 等入口统一编排。

面对这种跨环境场景,测试策略虽然各有差异,但都共享几个原则:(不依赖磁盘 I/O)、隔离(用例之间互不污染)、可回收(不留后台任务与事件监听)、可模拟(单进程内复现多实例并发)。下文逐一展开。

单元测试首选memoryRxStorage

对于单元测试,官方建议一律使用memoryRxStorage。它把数据完全保存在内存中,带来两大直接优势:

  • 速度:内存读写远快于落盘,测试套件整体耗时显著下降;
  • 隔离性:每次测试运行都从干净状态开始,无需在用例之间手动删除数据库文件。

基本用法:

import { createRxDatabase } from 'rxdb'; import { getRxStorageMemory } from 'rxdb/plugins/storage-memory'; const db = await createRxDatabase({ name: 'test-db', storage: getRxStorageMemory() });

从源码看,getRxStorageMemory()的实现位于 src/plugins/storage-memory/index.ts,它返回一个名为'memory'RxStorage对象,并通过模块级常量COLLECTION_STATES维护所有集合状态:

/** * Keep the state even when the storage instance is closed. * This makes it easier to use the memory storage * to test filesystem-like and multiInstance behaviors. */ const COLLECTION_STATES = new Map();

这段注释直接点明了内存存储在测试中的两个关键定位:filesystem-like(存储关闭后状态仍保留,便于观察)与multiInstance(同一进程内多个实例共享状态,这正是后面多标签页模拟的基础)。内存存储内部通过getMemoryCollectionKey(databaseName, collectionName, schemaVersion)(见 src/plugins/storage-memory/memory-helper.ts)构造唯一键,并将文档按索引字符串有序组织,因此查询行为与真实存储保持一致,测试结论可以放心外推。

using关键字自动关闭数据库

RxDB 完整支持 TypeScript 5.2+ 的Explicit Resource Management特性(using/await using声明)。开启该特性后,当变量离开作用域时,RxDB 会自动调用db.close(),这对测试场景堪称完美——你不再需要手动await db.close(),也无需担心忘记关闭导致资源泄漏。

describe('my test suite', () => { it('should insert a document', async () => { // Did you know? // using 'using' ensures db.close() is called automatically // at the end of the test function. await using db = await createRxDatabase({ name: 'test-db', storage: getRxStorageMemory() }); await db.addCollections({ ... }); // ... run your tests }); });

这一模式在仓库自身的测试中已被广泛采用,例如 test/unit/rx-database.test.ts 中的用例就是通过await using db = await createRxDatabase(...)创建数据库,函数结束后资源自动释放。

需要说明的适用前提:

  • 需要 TypeScript 5.2+,并在编译目标或 lib 中启用using声明的支持;
  • await using会在作用域结束时等待异步清理完成,适合数据库这种需要异步 close 的资源;
  • 该机制依赖语言层面的资源管理约定,Node.js 与主流构建工具链(Vite、Webpack 等)均已支持。

测试后的清理与dbCount()校验

大量测试并行/串行运行后,必须确保所有数据库都被正确关闭。遗留未关闭的RxDatabase实例会显著拖慢后续测试——因为后台任务和事件监听器仍然活跃,占用内存与事件循环。

官方给出的良好实践是:在测试收尾时断言没有任何数据库实例残留。RxDB 在rxdb/plugins/core中导出了dbCount()函数:

import { dbCount } from 'rxdb/plugins/core'; import assert from 'assert'; describe('cleanup', () => { it('ensure every db is cleaned up', () => { assert.strictEqual(dbCount(), 0); }); });

源码层面,dbCount()的实现在 src/rx-database.ts,它直接返回模块级计数器DB_COUNT

export function dbCount(): number { return DB_COUNT; }

这个计数器由RxDatabaseBase的构造函数与关闭流程精确维护(见 src/rx-database.ts 的初始化、src/rx-database.ts 的DB_COUNT++与 src/rx-database.ts 的DB_COUNT--)。也就是说,只要所有数据库都被 close,dbCount()必然归零;若某个用例泄漏了实例,该断言会立即失败,帮助你定位到"谁忘了关闭"。把它放在测试套件的末尾(类似仓库中的 test/unit/last.test.ts 角色),即可把清理问题提前暴露在 CI 中。

单进程内的多标签页(Multi-Tab)模拟

RxDB 的 Leader Election(leader-election.md)、Replication(replication.md)等能力都依赖多实例之间的协调。要在单个 Node.js 进程或单个测试运行器内验证这些行为,可以创建同名同存储的多个RxDatabase实例——RxDB 会把它们当作运行在不同标签页/进程中的实例来处理。

关键前提:必须设置ignoreDuplicate: true,否则同一进程内不允许创建多个同名数据库。

import { createRxDatabase } from 'rxdb'; import { getRxStorageMemory } from 'rxdb/plugins/storage-memory'; // Simulate Tab 1 const db1 = await createRxDatabase({ name: 'test-db', // same name storage: getRxStorageMemory(), ignoreDuplicate: true // must be set to true }); await db1.addCollections({ ... }); // Simulate Tab 2 const db2 = await createRxDatabase({ name: 'test-db', // same name storage: getRxStorageMemory(), ignoreDuplicate: true // must be set to true }); await db2.addCollections({ ... }); // insert at "tab one" await db1.todos.insert({ id: "foobar"}); // read at "tab two" const doc = await db2.todos.findOne("foobar").exec(true); assert.ok(doc);

这段代码之所以成立,是因为memory存储(以及其他同类存储)在同一个 JavaScript 进程内是共享的——正如前文源码中COLLECTION_STATES所示,集合状态存放在模块级 Map 中,跨实例可见。

从 src/rx-database.ts 的实现可以看到ignoreDuplicate的完整语义:

if (ignoreDuplicate) { if (!overwritable.isDevMode()) { throw newRxError('DB9', { database: name }); } } else { // check if combination already used throwIfDatabaseNameUsed(name, storage); }

两点必须注意的约束:

  1. 仅限 dev-modeignoreDuplicate只允许在开发模式下使用。如果生产环境(isDevMode()为假)传入ignoreDuplicate: true,会抛出DB9错误。对应错误文案位于 src/plugins/dev-mode/error-messages.ts,其中明确提示:如果你是有意创建重复数据库以在单元测试中模拟多标签页行为,请设置ignoreDuplicate: true;同时该选项严禁用于生产环境
  2. multiInstance默认开启createRxDatabase的参数中multiInstance默认值为true(见 src/rx-database.ts),这是多实例协调生效的前提;测试中无需显式关闭它。

仓库中大量测试依赖这一模式,例如 test/unit/cross-instance.test.ts 就是通过ignoreDuplicate: true创建多实例来验证跨实例行为;test/replication-firestore.test.ts 等复制类测试同样如此。若想更贴近真实场景,还可以配合dev-mode插件(rxdb/plugins/dev-mode)运行测试,这样既能使用ignoreDuplicate,又能获得开发模式的额外校验(如 schema 检查、错误提示增强)。

更多测试参考

  • 上述所有模式在仓库 test/ 目录中均有真实用例可对照:单元测试见 test/unit/,复制类测试见test/replication-*.test.ts,React 相关见 test/react.test.ts 与 test/unit/reactivity.test.ts;
  • memory存储的完整能力与参数说明见 rx-storage-memory.md;
  • createRxDatabase的全部创建参数(含ignoreDuplicatemultiInstancecloseDuplicates等)见 rx-database.md;
  • 若需在浏览器环境(如 Karma)中运行测试,仓库在 config/karma.conf.cjs 中给出了可参考的测试运行配置。
  • 数据库
  • NoSQL
  • 嵌入式数据库
  • 实时数据库

【免费下载链接】rxdb

The local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/

项目地址:https://gitcode.com/gh_mirrors/rx/rxdb
点击查看免费下载

相关推荐

上一篇:主动学习实战:Awesome open>下一篇:如何零门槛实现本地服务全球共享:颠覆性解决方案揭秘

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询