LoginSignup
2
0

More than 3 years have passed since last update.

[Angular, Jest] TypeError: Cannot read property 'ɵprov' of undefined のエラーを解決

Last updated at Posted at 2020-11-03

要約

Angularのサービスクラスに対するテストコードを作っていたとき、以下のエラーでハマりました。

TypeError: Cannot read property 'ɵprov' of undefined

または

Can't resolve all parameters for HeroService: (?).

結論から言うと、原因は分かっていないのですが、サービスクラスの@Injectable内部の記述を以下のように変更することで解消しました。

@Injectable({ 
-  providedIn: 'root',
+  providedIn: 'root'
})
export class HeroService {

カンマ消しただけです・・・。
原因分かる方がいらっしゃったら、コメント欄で教えていただけると幸いです。

(2020.11.15 追記)
解決しました。(やはりカンマは関係ありませんでした。)
tsconfigcompilerOptionsに以下のパラメータを追加したらエラーが解消されました。

tsconfig.json
{
  "compilerOptions": {
+    "emitDecoratorMetadata": true
  },
}

こちらのjest-preset-angularによると、Angular8以上はCLIの設定によってメタデータが欠ける可能性があるそうです。

With Angular 8 and higher, a change to the way the Angular CLI works may be causing your metadata to be lost

この設定を追加して実行すると、テストが通りました。
ちなみに、設定を変更したのに変わらない場合は、キャッシュを無効にして実行すると良いかもしれません。

$ jest hero.service --no-cache

(追記おわり)


環境

$ ng --version
Angular CLI: 10.1.7
Node: 12.16.1
OS: win32 x64

Angular: 10.1.6
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Ivy Workspace: Yes

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1001.7
@angular-devkit/build-angular   0.1002.0
@angular-devkit/core            10.1.7
@angular-devkit/schematics      10.1.7
@angular/cli                    10.1.7
@schematics/angular             10.1.7
@schematics/update              0.1001.7
rxjs                            6.6.3
typescript                      4.0.5

経緯

Angularのチュートリアル「Tour Of Heroes」の「4. サービスの追加」にあるサンプルコードに対してテストコードを実装しようとしていました。

テスト対象のクラスは以下。ダウンロードしたファイルそのままです。

hero.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
import { MessageService } from './message.service';

@Injectable({
  providedIn: 'root',
})
export class HeroService {

  constructor(private messageService: MessageService) { }

  getHeroes(): Observable<Hero[]> {
    // TODO: send the message _after_ fetching the heroes
    this.messageService.add('HeroService: fetched heroes');
    return of(HEROES);
  }
}

それに対して、以下のようなテストコードを実装。

hero.service.spec.ts

import { TestBed } from '@angular/core/testing';
import { HeroService } from 'src/app/hero.service';

describe('TestHeroService', () => {
    let heroService: HeroService;

    beforeEach(() => {
        TestBed.configureTestingModule({});
        heroService = TestBed.inject(HeroService);
    });

    it('should be created', () => {
        expect(heroService).toBeTruthy();
    });
});

インスタンス化されることをサクッとテストして、メソッドのテストを書くつもりだったのですが、冒頭の通り

TypeError: Cannot read property 'ɵprov' of undefined

というエラーが発生しました。
調べてみると正しくDIができていないようなのですが、はっきりとした原因は分かりませんでした。

解決への道のり

以下のような経緯でとりあえず解決しました。

  1. HeroServiceにおいて、MessageServiceをDIしないようにコメントアウトした。
  2. エラーが解消したので、やはりDIが関係していると判断
  3. Angular CLIを用いて別のサービス(TestService)を作成。HeroServiceと同様にMessageServiceをDIした上で同様のテストコードを作成。
  4. TestServiceのテストコードが通った。
  5. HeroServiceとTestServiceを比較して、providedInの部分が異なることを発見し、修正したらHeroServiceのテストコードが通った。

修正後のコードはこちらです。
hero.service.spec.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
import { MessageService } from './message.service';

@Injectable({
  providedIn: 'root' // カンマを消しただけ
})
export class HeroService {

  constructor(private messageService: MessageService) { }

  getHeroes(): Observable<Hero[]> {
    // TODO: send the message _after_ fetching the heroes
    this.messageService.add('HeroService: fetched heroes');
    return of(HEROES);
  }
}

2
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
2
0