Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What is going on with this article?
@pittanko_pta

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

More than 1 year has passed since last update.

この記事は 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:

0
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
pittanko_pta
Perfumeと乃木坂・欅坂が好きです。フロントエンドの人。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
0
Help us understand the problem. What is going on with this article?