表单脏检查在现代 JS 中需验证异步状态下 isDirty 值的准确性:用户修改后为 true,重置后为 false,防抖/微任务中保持一致且不重复触发;测试应使用 vi.waitFor 或 Promise.resolve() 等处理 microtask,结合 vi.useFakeTimers() 控制时间流,并覆盖重置、远程校验失败等边界场景。

表单脏检查(Dirty Checking)本身是 AngularJS 的核心机制,现代 JavaScript(包括 React、Vue、纯 DOM 应用)并不内置“脏检查”,而是依赖显式状态变更或响应式系统。如果你在用原生 JS 或现代框架做表单状态追踪,并希望测试“异步队列更新”(比如防抖提交、批量校验、微任务延迟同步等),关键不是模拟 AngularJS 的 $digest 循环,而是验证:状态是否在预期时机被正确捕获、更新和响应。
明确你要测的“脏”逻辑
先定义清楚什么是“脏”:
- 用户修改了输入框内容,但尚未提交 → isDirty = true
- 表单重置后 → isDirty = false
- 异步操作(如远程校验、防抖保存)触发期间,状态是否保持一致?是否避免重复触发?
不要直接测“有没有脏”,而要测:isDirty 的值是否在事件、Promise、setTimeout、queueMicrotask 等异步节点后符合预期。
用 Jest / Vitest 模拟异步时机
假设你封装了一个 useFormState Hook 或 Class,内部用 queueMicrotask 延迟更新脏状态:
立即学习“Java免费学习笔记(深入)”;
function markAsDirty() {
isDirty = true;
queueMicrotask(() => {
updateUI(); // 或触发事件
});
}测试写法(Vitest 示例):
test('should set isDirty to true after microtask', async () => {
const form = new MyForm();
expect(form.isDirty).toBe(false);
<p>form.setInputValue('test');
expect(form.isDirty).toBe(false); // 同步未更新</p><p>await vi.runOnlyPendingTimers(); // 不够 —— microtask 需用 nextTick
// ✅ 正确方式:
await vi.waitFor(() => expect(form.isDirty).toBe(true));
});注意:vi.runOnlyPendingTimers() 只处理宏任务(setTimeout/setInterval),microtask(Promise.then、queueMicrotask)需用 await vi.waitFor 或 await Promise.resolve()。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
测试防抖/节流更新队列中的状态一致性
如果脏状态更新被防抖(如 300ms 内多次输入只触发一次):
- 连续调用
markAsDirty()3 次,间隔 100ms → 最终只应触发 1 次更新 - 第 1 次调用后立即取消(
debounce.cancel())→ 不应更新
用 vi.useFakeTimers() 控制时间流:
vi.useFakeTimers();
form.setInputValue('a');
form.setInputValue('ab');
form.setInputValue('abc');
vi.advanceTimersByTime(299); // 还没触发
expect(form.isDirty).toBe(false);
<p>vi.advanceTimersByTime(1); // 触发
await vi.waitFor(() => expect(form.isDirty).toBe(true));监听并断言 DOM 或事件是否按队列时机响应
真实场景中,“脏”常关联 UI 反馈(如按钮变色、显示“* 已修改”)或自定义事件:
form.addEventListener('dirtychange', (e) => console.log(e.detail.isDirty));
测试时可:
- 用
vi.fn()mock 事件处理器 - 触发输入 →
await flushPromises()(若内部用了 Promise)或await vi.waitFor - 断言 handler 被调用且参数正确
小技巧:封装 flushPromises = () => Promise.resolve(),在需要“清空所有 pending promise”的地方 await 它。
不复杂但容易忽略:脏状态是否可逆、是否受异步副作用污染(例如远程校验失败后误清 isDirty)、是否在表单 reset 时彻底归零——这些才是异步队列测试的核心边界。

















