如何在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
相关产品推荐
相关产品推荐

