8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AngularAdvent Calendar 2019

Day 19

Angular 8.1.0 から入った createAngularJSTestingModule を読んでみる

Last updated at Posted at 2019-12-18

この記事は Angular Advent Calendar 2019 19日目の記事です。

はじめに

Angular 9.0のリリースが2020年になるなんて話がつい先日ありましたが、今もAngularJSが現役で動いているプロジェクトはまだまだあるんじゃないでしょうか。
僕が参加しているプロジェクトもその中のひとつで、現在はAngularJS + Angular 8のハイブリッド構成で動いています。今も絶賛移行中です:sob:

さて、Angular 8.1.0のリリースで createAngularJSTestingModulecreateAngularTestingModule がリリースされたことをご存知でしょうか?

AngularJSとAngularがハイブリッドで動いている環境での、ユニットテストをサポートするためのHelper functionなわけですが
今日はその中の createAngularJSTestingModule について見ていこうと思います :eyes:

うちのチームで動かしているハイブリッドアプリケーションのテストがたまに通らないことがあり、そもそもテストではどんなことをやってるのかを見てみたかったというのがキッカケです。

ざっくり使いどころを説明

Angularで書かれたServiceをdowngradeしてAngularJSで使う場合、
テストの中でもハイブリッドのアプリケーションをbootstrapしてあげる必要がありました。
しかし、新しく追加された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 の Service を 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 ではアプリケーションのbootstrapが済んでいるかをチェックしており
ここをパスするための必要最低限のことを行っていることがわかりました :bulb:

あとがき

本当であれば、業務で使っているコードをいい感じのサンプルプロジェクトに落とし込むのがいいんですが、うまくテストが動かない部分もあり断念しました :sob:

Angularのバージョンがもうすぐ9になろうとしているのに、Angularチームは継続してAngularJSからのマイグレーションをサポートするためのツールをリリースしてくれました。感謝しかないです :pray:

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?