阅读从Angular 8.1.0版本开始引入的createAngularJSTestingModule
本篇文章是Angular Advent Calendar 2019的第19篇。
首先
最近才传出了 Angular 9.0 将在 2020 年发布的消息,但是是否还有很多项目仍在使用 AngularJS 来运行呢?
我参与的项目就是其中之一,目前以 AngularJS + Angular 8 的混合结构在运行。我们目前正在积极进行迁移的工作。
嗯,你知道在Angular 8.1.0版本中发布了 createAngularJSTestingModule 和 createAngularTestingModule 吗?
今天我们来看一下在AngularJS和Angular混合环境中支持单元测试的Helper函数,即createAngularJSTestingModule。
我們團隊中有時候測試我們運行的混合應用程式會失敗,所以我想看看測試到底在做些什麼,這是我的動機。
大致解释其用途。
如果您想将使用Angular编写的Service降级并在AngularJS中使用,那么在测试中您仍然需要引导混合应用程序。然而,通过使用新添加的createAngularJSTestingModule,您不再需要这样做。看起来您可以更简单地编写测试,并且能够更快地执行测试。
考试中
beforeEach(module(createAngularJSTestingModule([Ng2AppModule])));
beforeEach(module(ng1AppModule.name));
当写下这个,您就可以从AngularJS的Injector中取出Angular的Service。可能是这种感觉↓↓
it('should have access to the HeroesService',
inject((heroesService: HeroesService) => {
expect(heroesService).toBeDefined();
})
);
让我看一下代码。
这是在Angular 8.2.14版本中的@angular/upgrade/static/testing中找到的createAngularJSTestingModule函数的源代码。
export function createAngularJSTestingModule(angularModules: any[]): string {
return ng.module_('$$angularJSTestingModule', [])
.constant(UPGRADE_APP_TYPE_KEY, UpgradeAppType.Static) // '$$angularUpgradeAppType', 2
.factory(
INJECTOR_KEY, // '$$angularInjector'
[
$INJECTOR, // '$injector'
($injector: ng.IInjectorService) => {
TestBed.configureTestingModule({
imports: angularModules,
providers: [{provide: $INJECTOR, useValue: $injector}]
});
return TestBed.get(Injector);
}
])
.name;
}
在这里正在做的是:
– 创建一个名为$$angularJSTestingModule的AngularJS模块
– 在名为$$angularUpgradeAppType的AngularJS constant中指定了2(UpgradeAppType.Static)
– 使用TestBed.configureTestingModule生成测试模块…
– 以名为$$angularInjector的形式将Angular的Injector提供给AngularJS
这样说也许有人会想:“从AngularJS中没有使用名称为$$angularInjector的DI”。但接下来我要展示的downgradeInjectable是关键。downgradeInjectable是一个必要的辅助函数,用于将Angular的服务降级到AngularJS中使用。
@Injectable()
export class AwesomeService { ... }
ng1App.factory('AwesomeService', downgradeInjectable(AwesomeService) as any)
根据此,我会以这样的感觉来写。然后可以去查看 downgradeInjectable 的实现…
// 第2引数の downgradedModule は使っていないです
export function downgradeInjectable(token: any, downgradedModule: string = ''): Function {
const factory = function($injector: IInjectorService) {
const injectorKey = `${INJECTOR_KEY}${downgradedModule}`; // '$$angularInjector'
// isFunction = (t) => typeof t === 'function'
// なので、ここではgetTypeNameの結果 'AwesomeService' が使われます(ログ用)
const injectableName = isFunction(token) ? getTypeName(token) : String(token);
const attemptedAction = `instantiating injectable '${injectableName}'`;
// ただしくupgradeされているアプリケーションかチェックする(雑に書いてます)
// createAngularJSTestingModule内で '$$angularUpgradeAppType' を 2 として定義したのは
// ここのチェックを通すためだと思われる。本来であればbootstrapをしないと通せない場所。
validateInjectionKey($injector, downgradedModule, injectorKey, attemptedAction);
// AngularJSの世界に定義してあるAngularのInjectorを取得する
const injector: Injector = $injector.get(injectorKey);
return injector.get(token); // そこからAwesomeServiceを取得して返す
};
(factory as any)['$inject'] = [$INJECTOR];
return factory;
}
感觉就是这样了。
在Angular的 downgradeInjectable 中,它会检查应用程序是否已经引导完成,
我们发现它会做必要的最低限度的事情来通过这一点。
作者的话
如果可以的话,将我在工作中使用的代码转化为一个好的示例项目是很不错的,但是由于测试部分无法正常运行,所以我放弃了。
尽管Angular版本即将升级至9,但Angular团队仍然发布了一些工具,以支持从AngularJS迁移到新版本。我们只能表达感激之情。