You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Ember单元/集成测试中枚举所有路由名称

在Ember测试中枚举所有路由名称的方案

我之前刚好帮团队解决过类似的需求——确保新增路由都带上必填元数据,下面分享几个在单元/集成测试里可行的方法,亲测有效:

一、单元测试方案(推荐,速度快)

单元测试不需要启动整个应用,直接测试你的router.js模块就行。核心思路是实例化Router类,通过它的路由识别器(recognizer)遍历所有路由:

import { module, test } from 'qunit';
import Router from 'your-app-name/router';
import { setupTest } from 'your-app-name/tests/helpers';
import { configureRouter } from 'your-app-name/router'; // 提前把路由map逻辑抽成单独函数

module('Unit | Router | Enumerate Routes', function(hooks) {
  setupTest(hooks);

  test('all routes have required metadata', function(assert) {
    // 创建Router实例并复用项目中的路由配置
    const router = new Router({ location: 'none' });
    configureRouter(router); // 调用抽离的路由配置函数

    // 初始化路由识别器
    router.setupRouter();

    // 递归遍历路由树的辅助函数
    function collectRoutes(handlers, parentPath = '') {
      let allRoutes = [];
      handlers.forEach(handler => {
        const fullPath = parentPath + handler.path;
        allRoutes.push({
          name: handler.name,
          path: fullPath
        });

        // 递归收集子路由
        if (handler.children?.length) {
          allRoutes = [...allRoutes, ...collectRoutes(handler.children, fullPath + '/')];
        }
      });
      return allRoutes;
    }

    // 获取所有路由列表
    const allRoutes = collectRoutes(router.recognizer.handlers);

    // 检查每个路由的必填元数据
    allRoutes.forEach(route => {
      const routeClass = this.owner.lookup(`route:${route.name}`);
      assert.ok(routeClass.get('requiredMeta'), `Route ${route.name} is missing required metadata`);
    });
  });
});

小提示:把router.js里的map逻辑抽成单独导出的函数,能避免测试代码和业务代码同步修改的麻烦。

二、集成测试方案(适合依赖完整上下文的场景)

如果你的元数据依赖应用容器里的服务,或者需要更真实的运行环境,可以用集成测试:

import { module, test } from 'qunit';
import { setupApplicationTest } from 'your-app-name/tests/helpers';

module('Integration | Router | Enumerate Routes', function(hooks) {
  setupApplicationTest(hooks);

  test('all routes have required metadata', async function(assert) {
    // 等待应用启动完成
    await this.visit('/');

    // 从容器获取主router实例
    const router = this.owner.lookup('router:main');

    // 复用递归收集路由的函数
    function collectRoutes(handlers, parentPath = '') {
      let allRoutes = [];
      handlers.forEach(handler => {
        const fullPath = parentPath + handler.path;
        allRoutes.push({
          name: handler.name,
          path: fullPath
        });

        if (handler.children?.length) {
          allRoutes = [...allRoutes, ...collectRoutes(handler.children, fullPath + '/')];
        }
      });
      return allRoutes;
    }

    const allRoutes = collectRoutes(router.recognizer.handlers);

    // 检查元数据,也可直接读取路由定义中的meta字段
    allRoutes.forEach(route => {
      const routeInstance = this.owner.lookup(`route:${route.name}`);
      assert.ok(routeInstance.requiredMeta, `Route ${route.name} missing required metadata`);
    });
  });
});

三、额外优化:过滤无需检查的路由

如果有内置路由(比如error、loading)不需要检查,可以在收集后过滤:

const excludedRoutes = ['error', 'loading'];
const routesToCheck = allRoutes.filter(route => !excludedRoutes.includes(route.name));

内容的提问来源于stack exchange,提问作者Chris de Almeida

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:06:49