Ngrxセレクターによって公開された値を返すサービスがあります。コンポーネントは、データを取得するためにこのサービスを定義します。サービスのモックを使用してコンポーネントの単体テストを作成していて、各単体テストに対して異なる値を返すためにモックサービスが必要です。どうすればこれを達成できますか?
コンポーネント
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
providers: [TestService],
})
export class TestComponent {
test$ = this.testService.test$;
test: number;
constructor(private service: TestService) {
service.test$.subscribe(test => this.test = test);
}
}
サービス
export class TestService {
test$ = this.store.select(getTestValueFromStore);
constructor(private store: Store<any>){}
}
試行1(サービスの値をリセット):機能しません
class MockTestService {
**test$ = of(10);**
}
describe('TestComponent', () => {
let testService: TestService;
beforeEach((() => {
// Define component service
TestBed.overrideComponent(
TestComponent,
{ set: { providers: [{ provide: TestService, useClass: MockTestService }] } }
);
TestBed.configureTestingModule({
declarations: [TestComponent]
})
.compileComponents();
}));
beforeEach(async() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
testService = fixture.debugElement.injector.get(TestService);
fixture.detectChanges();
});
it('should do something when the value returned by the service is 20', fakeAsync(() => {
**testService.test$ = of(20);**
tick();
expect(component.test).toEqual(20);
}));
});
試行2:サブジェクトを使用。TestServiceはサブジェクトではなくオブザーバブルを返すため、カルマがエラー「プロパティ 'next'はタイプ 'Observable'に存在しません)をスローします
class MockTestService {
**test$ = new BehaviourSubject(10);**
}
describe('TestComponent', () => {
let testService: TestService;
beforeEach((() => {
// Define component service
TestBed.overrideComponent(
TestComponent,
{ set: { providers: [{ provide: TestService, useClass: MockTestService }] } }
);
TestBed.configureTestingModule({
declarations: [TestComponent]
})
.compileComponents();
}));
beforeEach(async() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
testService = fixture.debugElement.injector.get(TestService);
fixture.detectChanges();
});
it('should do something when the value returned by the service is 20', fakeAsync(() => {
**testService.test$.next(20);**
tick();
expect(component.test).toEqual(20);
}));
});
質問で、「模擬サービスはユニットテストごとに異なる値を返す」ようにしたいと言っていました。これを行うには、コンポーネントの定義方法にいくつかの変更を加える必要があります。
最も重要な変更は、サブスクリプションをngOnInit()に移動し、コンポーネントのコンストラクターから移動することです。これを行うと、サブスクリプションがいつ起動するかを制御できます。それ以外の場合は、コンポーネントの作成時に起動され、ユニットテストごとに異なる値を返すことが非常に困難になります。
それが終わったら、fixture.detectChanges()
を呼び出す場所に注意する必要があります。これはngOnInit()
を実行するためです。サブスクリプションが実行され、Observableから返された値が_component.test
_に格納されます。 of()
を使用しているため、これは単純に1回発行されてから完了することに注意してください。 _testService.test$
_に新しい値を入力しても、再度発行されません。
これを設定したら、fixture.detectChangesを呼び出す前に_testService.test$
_の値を変更して、値を好きなようにすることができます。
これを実証するために StackBlitz を設定しました。コードを変更して、機能させるだけです。 2つのテストがあり、それぞれ異なる値が返されます。
これが役に立てば幸いです!
このようなものを試してください
_describe('TestComponent', () => {
beforeEach((() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
providers: [
{ provide: TestService, useValue: { test$: of(20) } }
]
})
.compileComponents();
}));
beforeEach(async() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
testService = fixture.debugElement.injector.get(TestService);
fixture.detectChanges();
});
it('should do something when the value returned by the service is 20', () => {
expect(component.test).toEqual(20);
});
});
_
of()
は同期的に発行されるため、ここではfakeAsyncを使用する必要はありません。
また、ngOnInit
フックでオブザーバブルをサブスクライブすることを検討してください。コンストラクターは、依存性注入にのみ使用する必要があります。