← 解説「HTTPを使うサービスのテスト — 本物のAPIは呼ばずに検証する」に戻る
サンプルコードで身につける: HTTPを使うサービスのテスト — 本物のAPIは呼ばずに検証する
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1テスト対象のサービスと道具立て
テスト対象は HttpClient でAPIを呼ぶ普通のサービスです。テスト側では providers に2つの関数を登録するだけで、通信先が偽物に差し替わります。サービス側のコードは一切変更しません。
// user.service.ts — テスト対象(普通のサービス。変更は不要)
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
getUsers(): Observable<string[]> {
return this.http.get<string[]>('/api/users');
}
}
// テスト側のセットアップ — これだけで通信が偽物に差し替わる
TestBed.configureTestingModule({
providers: [
provideHttpClient(), // 先: 本物のHttpClient一式
provideHttpClientTesting(), // 後: 通信部分だけ偽物に上書き
],
});
2expectOne と flush の基本の流れ
サービスのメソッドを呼ぶとリクエストが偽の通信層に溜まります。expectOne で捕まえ、flush で偽レスポンスを返すと subscribe のコールバックが動きます。呼ぶ順番がそのまま通信の一生を表しています。
it('一覧を取得できる', () => {
const service = TestBed.inject(UserService);
const httpTesting = TestBed.inject(HttpTestingController);
// 1. メソッドを呼ぶ(リクエストは偽の通信層に溜まる)
let result: string[] = [];
service.getUsers().subscribe((users) => (result = users));
// 2. 「このURLに1件飛んだはず」と捕まえる
// (0件でも2件でもテストは失敗する)
const req = httpTesting.expectOne('/api/users');
// 3. 偽のレスポンスを返す → subscribe の中が動く
req.flush(['田中', '鈴木']);
// 4. 結果を検証
expect(result).toEqual(['田中', '鈴木']);
});
3POSTリクエストの中身まで検証する
req.request にはメソッドや送信ボディが入っています。「正しいURLに、正しい中身で送っているか」まで検証できるので、送信系の処理こそHTTPテストの価値が出ます。
it('ユーザー登録は正しいボディでPOSTされる', () => {
const service = TestBed.inject(UserService);
const httpTesting = TestBed.inject(HttpTestingController);
service.register('田中', 30).subscribe();
const req = httpTesting.expectOne('/api/users');
// メソッドの検証
expect(req.request.method).toBe('POST');
// 送信ボディの検証(オブジェクトなので toEqual)
expect(req.request.body).toEqual({ name: '田中', age: 30 });
req.flush({ id: 1 });
});
4エラーレスポンスの処理をテストする
flush にステータスコードを添えれば、サーバーエラーも自由に再現できます。本物のサーバーを落とさなくても「500が返ったらエラーメッセージを出す」という異常系を確実にテストできます。
it('サーバーエラー時はエラー用の処理が動く', () => {
const service = TestBed.inject(UserService);
const httpTesting = TestBed.inject(HttpTestingController);
let errorMessage = '';
service.getUsers().subscribe({
next: () => {},
error: () => (errorMessage = '取得に失敗しました'),
});
// 偽の500エラーを返す(本物のサーバーは不要)
const req = httpTesting.expectOne('/api/users');
req.flush('server error', {
status: 500,
statusText: 'Internal Server Error',
});
expect(errorMessage).toBe('取得に失敗しました');
});
5afterEach の verify で検証漏れを防ぐ
verify は「捕まえていないリクエストが残っていないか」を確認します。afterEach に置けば全テストで自動的に検証され、意図しない二重リクエストなどのバグを取りこぼさなくなります。
describe('UserService', () => {
let httpTesting: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [provideHttpClient(), provideHttpClientTesting()],
});
httpTesting = TestBed.inject(HttpTestingController);
});
afterEach(() => {
// 各テストの最後に「未処理のリクエストが残っていないか」を確認。
// 想定外のAPI呼び出し(二重リクエストなど)があると失敗して教えてくれる
httpTesting.verify();
});
it('...', () => {
// 各テストは expectOne / flush の流れを書くだけ
});
});