IndexedDB事务具备原子性,即同一事务中若任一操作失败(如插入重复主键),整个事务将回滚,所有操作均不生效。验证需在单事务内执行成功与失败操作,监听transaction.onabort并检查数据库状态是否完全回滚。

要验证 IndexedDB 事务是否真正具备原子性,不能只看“数据写进去了没”,而必须构造能触发失败的场景,观察“部分操作失败时,其他操作是否被自动撤销”。核心思路是:在同一个事务中混入必然失败的操作,然后检查数据库最终状态是否完全干净。
构造强制失败的原子性测试用例
原子性的本质是“全成功或全回滚”,所以测试关键在于制造可控失败。最可靠的方式是在事务中插入一条违反主键约束(或唯一索引)的数据:
- 先确保对象存储(Object Store)设置了 keyPath 或 autoIncrement,并启用 unique 约束(如对 name 字段建唯一索引)
- 事务内先 add 一条正常数据,再 add 一条相同 key 的重复数据(触发 DOMException: "Key already exists")
- 不监听单个 request.onerror,而是只关注 transaction.onabort —— 若触发,说明事务已中止,前一条数据也不应存在
验证步骤与预期结果
执行后需人工或脚本验证三件事:
- 事务 onabort 是否被调用(而非 oncomplete)
- 使用另一个只读事务查询该 store,确认第一条数据未被写入(即数据库状态与事务前完全一致)
- 检查 indexedDB.deleteDatabase 后重建,再重放操作,结果可复现 —— 排除缓存或残留状态干扰
避免常见误判陷阱
很多“看似成功”的测试其实没测出原子性,原因常是:
- 用了多个事务:每条 add() 都 new 一个 transaction → 实际是 N 个独立事务,失败只影响当次,不回滚前面的
- 监听了 request.onsuccess 却忽略 transaction.onabort → 单个请求成功不等于事务成功
- 测试数据量小但没加索引 → 重复 key 不报错,事务“意外”完成,误判为原子
- 未清空数据库就反复跑测试 → 上次残留数据干扰判断
自动化断言建议(代码级)
可封装成可复用的 testAtomicity 函数:
- 打开数据库,创建带唯一索引的 store(如 users,name 唯一)
- 启动 readwrite 事务,获取 store,依次调用 store.add({ id: 1, name: 'Alice' }) 和 store.add({ id: 2, name: 'Alice' })
- 返回 Promise,resolve(true) 仅当 onabort 触发且后续查询确认无 id=1 记录
- 配合 Jest 或 Vitest,用 expect(await testAtomicity()).toBe(true) 断言

















