编写自动化测试

运行随源码下载的测试,覆盖业务、取消与失败。

扩展 API v1按当前源码核对
本页目录

测试分成三层#

层次 目标 不能替代什么
纯逻辑自动化 输入、状态、取消与错误分支正确 不能证明真实文件 IPC 和窗口可用
桌面集成 实际宿主中打开、调用、保存和清理 不能证明正式包签名和授权登记正确
正式发行验收 用户实际收到的包可导入、更新和执行 不能仅用源码运行结果代替

先运行随示例提供的测试,再按自己的业务补充。不要只断言“函数存在”,也不要把实现逐行抄成断言来获得通过。

效率插件的业务测试#

在文本整理示例根目录执行:

powershell
node --test .tests/text.test.cjs
javascript
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');

function load() {
  const context = {};
  context.window = context;
  vm.runInNewContext(fs.readFileSync(
    path.join(__dirname, '../modules/text.js'), 'utf8'), context);
  return context.MyExampleTool.normalize;
}

test('合并空格、换行和制表符,保留文本顺序', () => {
  assert.equal(load()('  alpha\n\t beta  '), 'alpha beta');
});
test('保留中文内容,空输入得到空字符串', () => {
  const normalize = load();
  assert.equal(normalize('  题目\n分析  '), '题目 分析');
  assert.equal(normalize('   '), '');
});
test('已经整理的结果重复处理不改变内容', () => {
  const normalize = load();
  const once = normalize(' A   B ');
  assert.equal(normalize(once), once);
});

测试直接加载下载包中的 modules/text.js,覆盖多种空白、中文、空输入和重复处理,防止例子与测试各自维护一份不同实现。

自动化任务的状态测试#

在资料统计示例根目录执行:

powershell
node --test .tests/actions.test.cjs
javascript
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');

function load() {
  const context = { setTimeout, clearTimeout };
  context.window = context;
  for (const name of ['state', 'actions']) {
    vm.runInNewContext(fs.readFileSync(path.join(__dirname,
      '../modules/' + name + '.js'), 'utf8'), context);
  }
  return context.MyExampleTask;
}

test('正常统计得到真实结果', async () => {
  const task = load(), state = task.createState();
  await task.run(state, '甲乙\n丙', () => {});
  assert.equal(state.status, 'completed');
  assert.deepEqual(JSON.parse(JSON.stringify(state.result)),
    { lines: 2, characters: 3 });
  assert.equal(state.processed, state.total);
});
test('空输入失败,并且没有伪造结果', async () => {
  const task = load(), state = task.createState();
  await task.run(state, '  ', () => {});
  assert.equal(state.status, 'failed');
  assert.match(state.error, /材料/);
  assert.equal(state.result, null);
});
test('分批运行中取消,旧任务不写回完成', async () => {
  const task = load(), state = task.createState();
  await task.run(state, 'a '.repeat(20000), () => {
    if (state.processed > 0) task.cancel(state, () => {});
  });
  assert.equal(state.status, 'cancelled');
  assert.equal(state.result, null);
  await task.run(state, '新任务', () => {});
  assert.equal(state.status, 'completed');
  assert.equal(state.result.characters, 3);
});
test('关闭后不让旧运行继续回写', async () => {
  const task = load(), state = task.createState();
  await task.run(state, 'x'.repeat(20000), () => {
    if (state.processed > 0) { state.disposed = true; state.runId++; }
  });
  assert.equal(state.result, null);
  assert.notEqual(state.status, 'completed');
});

测试覆盖正确结果、空输入、取消与关闭后旧运行不回写。它使用组件真实的 state 与 actions 模块,但仍是 Node 环境中的逻辑测试,不假装已经验证桌面桥接。

为自己的业务补充边界#

  • 输入:空材料、丢失文件、错误类型、长名称、编码和大数据。
  • 异步:重复开始、取消后立刻重试、旧请求晚到、窗口关闭。
  • 保存:路径不足、只读原件、写入失败、写入时又有新修改。
  • 状态:未知总量、不支持的状态版本、检查点缺文件。
  • 兼容:缺少接口、旧宿主、资源路径变化和旧项目迁移。

每个失败用例写清预期状态、保留哪些内容,以及应出现什么提示。不要只判断没有抛异常。

如何记录测试结果#

text
组件:my-example-task 1.0.0
宿主:实际测试版本
环境:开发版 / 正式版,操作系统与运行环境
用例:运行后立即取消,再次开始
预期:旧运行不覆盖新结果,输入可编辑
实际:记录观察与必要的脱敏日志
结论:通过 / 未通过

保存真实执行过的结果,不在测试未运行时填写通过。出问题时记录阶段和最小输入,不收集用户密码或 API Key。

宿主项目检查#

在宿主项目根目录执行 npm run check:project,再运行与改动有关的验证。组件独立测试通过,不代表宿主结构检查也通过;反过来也一样。

下一步到桌面集成与性能验收,把逻辑测试中的接口替身替换为真正的桌面操作。

下载本页 Markdown