Jestでngx-bootstrapを利用したコンポーネントのテストをする

 この記事は、[Business Bank Group Developers Advent Calendar](https://qiita.com/advent-calendar/2018/bbg_developers) 14日目の記事です。

# はじめに

最近プロダクトにJestを導入して、毎日感動しながらテストを作成していたのですが、ngx-bootstrapを利用したテストを動かそうとしたらエラーになって悩んでいたので解決までに対応したことを記そうと思います。

# 現象

##　その１

一般的なコンポーネントのようなテストを作成してテストを走らせたら以下のエラーが発生しました。
（固有名詞はマスクしています。辻褄合わなかったらすみません。）

```bash:
Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /Users/user/sample-app/node_modules/ngx-bootstrap/modal/index.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){export { MyModalComponent } from './my-modal.component';
                                                                                             ^^^^^^
    SyntaxError: Unexpected token export

      3 | } from '@angular/core';
    > 4 | import { ModalDirective } from 'ngx-bootstrap/modal';
        | ^
      5 |
      6 | @Component({
      7 |   selector: 'my-modal',
```

ngx-bootstrapも変換する必要があったので、以下のように変換対象外から外すようにしました。

```diff:jest.config.js
  transformIgnorePatterns: [
-    "<rootDir>/node_modules/(?!@ngrx|@progress)"
+    "<rootDir>/node_modules/(?!@ngrx|ngx-bootstrap|@progress)"
  ]
```


## その２

ngx-bootstrapのdropdownを利用したテストで以下のエラーが出ました。

```
No provider for TranslateStore!
```

実装部分にはDI注入の処理を書いてなかったので問題ないかと思っていたのですが、ちゃんと追加する必要がありました。

```diff:my-dropmenu.component.spec.ts
+ import { TranslateService, TranslateModule, TranslateLoader, TranslateFakeLoader } from '@ngx-translate/core';


beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [
+      TranslateModule.forRoot({
+        loader: { provide: TranslateLoader, useClass: TranslateFakeLoader },
+      }),
    ],
    providers: [
+      TranslateService,
    ],
```

以上のように追加しました。

## その3

```ts:my-dropmenu.component.spec.ts
    it('default', fakeAsync(() => {
      fixture.detectChanges();
      const toggle = fixture.debugElement.query(By.css('.dropdown-toggle'));
      toggle.triggerEventHandler('click', null);
      tick();
      fixture.detectChanges();
      const dropDownMenu = fixture.debugElement.queryAll(By.css('li'));
      expect(dropDownMenu.length).toBe(2);
    }));
```

このテストでは、dorpDownMenuの数が動的に生成される実装にしていたので、メニューの項目が正しい数になっているかのテストを作成しています。
しかし、結果は0となり、ドロップダウンが開かれていない状態になっていました。


```diff:my-dropmenu.component.spec.ts
+ import { BsDropdownModule } from 'ngx-bootstrap/dropdown';

beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [
+     BsDropdownModule.forRoot(),
      TranslateModule.forRoot({
        loader: { provide: TranslateLoader, useClass: TranslateFakeLoader },
      }),
    ],
    providers: [
      TranslateService,
    ],
```

dropdownメニューを押下した際にエラーが出なかったので詰まりましたが、これも単純にdropdownのmoduleをきちんとimportする必要がありました。

# さいごに

以上の対応で目的のテストを達成することができました。
jestがというよりangularのproviderの依存関係による問題というのはちょこちょこ出て来て詰まるなあと行った感想です。
これらはきちんと基底componentのmockなりstubなりを作成していれば、最初の1人が立ち向かえば解決するはずの問題なので、頑張って一つずつ解消していこうと思っています。

# 明日の担当

明日は @shota_yamashita さんの担当です。お願いします。