Angular 8.1.0부터 들어간 createAngularJSTestingModule 읽어보기

11994 단어 Angular
이 기사는 Angular Advent Calendar 2019 19일째 기사입니다.

소개



Angular 9.0 출시 2020년 무슨 이야기가 얼마 전 있었습니다만, 지금도 AngularJS가 현역으로 움직이고 있는 프로젝트는 아직 있는 것이 아닐까요.
제가 참여하고 있는 프로젝트도 그 중의 하나로, 현재는 AngularJS+Angular 8의 하이브리드 구성으로 움직이고 있습니다. 지금도 절찬 이행 중입니다

이제 Angular 8.1.0 릴리스에서 createAngularJSTestingModulecreateAngularTestingModule가 릴리스되었음을 알고 계십니까?

AngularJS와 Angular가 하이브리드로 움직이는 환경에서 유닛 테스트를 지원하기위한 Helper function입니다.
오늘은 그 안의 createAngularJSTestingModule에 대해 보고 싶습니다

우리 팀에서 움직이는 하이브리드 애플리케이션의 테스트가 가끔씩 통과하지 못할 수 있고, 원래 테스트에서는 어떤 일을 하고 있는지를 보고 싶었다는 것이 계기입니다.

자꾸 사용하는 곳을 설명



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 서비스를 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이 완료되었는지 확인합니다.
여기를 통과하는 데 필요한 최소한의 작업을 수행하는 것으로 나타났습니다

후기



사실이라면, 업무에서 사용하고 있는 코드를 좋은 느낌의 샘플 프로젝트에 떨어뜨리는 것이 좋지만, 잘 테스트가 움직이지 않는 부분도 있어 포기했습니다

Angular 버전이 곧 9가 되려고 노력했지만 Angular 팀은 AngularJS에서 마이그레이션을 지원하는 도구를 계속 출시했습니다. 고마워요

좋은 웹페이지 즐겨찾기