阅读从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迁移到新版本。我们只能表达感激之情。

bannerAds