TypeScriptで非同期処理を含むES6構文をテストする方法!初心者のための徹底解説
生徒
「TypeScriptで、後からデータが届くような『非同期処理』を使ったプログラムを書いたのですが、これが正しく動くかテストする方法を教えてください!」
先生
「最新のJavaScript(ES6以降)の書き方とTypeScriptを組み合わせた非同期処理のテストですね。実は、テスト専用の道具を使うと、未来の結果を待ってからチェックすることができるんですよ。」
生徒
「未来の結果を待つ……?難しそうですが、私にもできますか?」
先生
「大丈夫です!まずは『非同期処理』とは何かという基本から、具体的なテストの書き方まで順番に解説していきますね。」
1. 非同期処理とES6構文の基本を知ろう
プログラミングの世界には、「非同期処理(ひどうきしょり)」という考え方があります。これは、時間のかかる作業(例えば、インターネットからデータをダウンロードする、大きなファイルを読み込むなど)をしている間に、他の作業を止めることなく進める仕組みのことです。
日常生活で例えるなら、「洗濯機を回している間に、料理を作る」ようなものです。洗濯が終わるまでじっと洗濯機の前で待っているのが「同期処理」、洗濯機を動かしながら別のことをするのが「非同期処理」です。
現代のJavaScriptやTypeScript(これらを総称してES6やESNextと呼ぶことがあります)では、この非同期処理をスマートに書くための「Promise(プロミス)」や「async/await(エイシンク・アウェイト)」という便利な構文が用意されています。
2. テストとは「正解合わせ」のこと
プログラムを作ったら、それが自分の思った通りに動くか確認する必要があります。これを「テスト」と呼びます。特にTypeScriptでは、型というルールがあるためエラーは見つけやすいですが、中身の計算が合っているかどうかはテストコードを書いて確認するのが一般的です。
非同期処理のテストが難しいと言われる理由は、「結果が返ってくるまで時間がかかる」からです。テストプログラムが、結果を待たずに終了してしまうと、正しく計算できたのか判定できません。そのため、テストの世界でも「ちゃんと結果が出るまで待ってね」という命令を出す必要があります。
3. Jest(ジェスト)を使ったテストの準備
TypeScriptのテストで最も人気がある道具の一つが「Jest」です。これを使うと、「この関数の答えは10になるはずだ!」といったチェックを自動で行うことができます。
まずは、テスト対象となる簡単な非同期関数を作ってみましょう。今回は「3秒後に挨拶を返す」という簡単なプログラムを例にします。
// hello.ts というファイル名だとします
export const fetchGreeting = async (name: string): Promise<string> => {
// 実際にはここでインターネット通信などを行いますが、今回は擬似的に待ちます
return `こんにちは、${name}さん!`;
};
ここで出てきた単語を解説します。
- export(エクスポート):他のファイルからもこの関数を使えるようにする宣言です。
- async(エイシンク):この関数の中で「待ち時間」が発生することを伝えます。
- Promise(プロミス):「将来、文字列(string)を返すと約束します」という意味の型です。
4. 非同期処理のテストコードを書いてみよう
それでは、先ほどの関数をテストしてみましょう。テストコードでは、awaitキーワードを使って、関数が完了するのを待ちます。
// hello.test.ts
import { fetchGreeting } from './hello';
test('挨拶が正しく返ってくるか確認する', async () => {
// 関数が終わるのを待って、結果を受け取る
const result = await fetchGreeting('田中');
// 結果が予想通りかチェックする
expect(result).toBe('こんにちは、田中さん!');
});
テストを実行すると、次のような結果が表示されます。
PASS ./hello.test.ts
✓ 挨拶が正しく返ってくるか確認する (5ms)
このように、テストコード自体に async を付け、中身で await を使うことで、非同期処理を同期処理(普通の順番通りの処理)のように分かりやすく書くことができます。これがES6以降のモダンなテストの書き方です。
5. エラーが発生する場合のテスト方法
プログラミングでは、常に成功するとは限りません。通信エラーが起きた時に、正しくエラーを処理できるかも重要です。TypeScriptでは、エラーをテストする際も async/await が活躍します。
// エラーを投げる関数の例
export const fetchError = async () => {
throw new Error('データが見つかりませんでした');
};
// テストコード
test('エラーが正しく発生するか確認する', async () => {
// reject(失敗)することを期待する書き方
await expect(fetchError()).rejects.toThrow('データが見つかりませんでした');
});
ここで使った rejects は、「この関数は失敗するはずだ」ということをテストツールに伝えるための命令です。成功だけでなく失敗のパターンも用意しておくことが、バグのない強いプログラムを作るコツです。
6. なぜTypeScriptとES6の連携が重要なのか
TypeScriptは、古いJavaScriptの書き方よりも、ES6(2015年に登場した規格)以降の新しい書き方と非常に相性が良く作られています。非同期処理の Promise もその一つです。
以前の古い方法(コールバック関数と呼びます)では、処理が複雑になると「コールバック地獄」と呼ばれる、迷路のような読みにくいコードになっていました。しかし、ES6の async/await を使うことで、上から下へ流れるような読みやすいコードが書けるようになり、テストも劇的に楽になったのです。
TypeScriptを使うことで、「この非同期処理はどんなデータを返すのか?」が事前にハッキリするため、テストを書く前から間違いに気づけるというメリットもあります。
7. 実際の開発でよく使う「モック」という技術
初心者の皆さんが今後学習を進めると、「モック(Mock)」という言葉に出会うでしょう。これは「偽物」という意味です。テストのたびに本物のインターネットに接続してデータを取ってくると、時間がかかったり、相手のサーバーに迷惑をかけたりします。
そこで、テストの時だけ「本物のフリをする偽物の関数」に差し替えるのがモックです。このモックも非同期処理のテストでは頻繁に使われます。例えば、「3秒待つフリをして、すぐ結果を返す」といった設定が可能です。
// 偽物のデータを作る例
const mockFetch = jest.fn().mockResolvedValue('偽物のデータ');
test('モックを使ったテスト', async () => {
const data = await mockFetch();
expect(data).toBe('偽物のデータ');
});
最初は難しく感じるかもしれませんが、「テストを速く正確に終わらせるための工夫」だと考えてください。
8. まとめに向けて:テストを書く習慣をつけよう
TypeScriptで非同期処理のテストができるようになると、一人前のエンジニアへの道が大きく開けます。最初は async と await をどこに書けばいいか迷うかもしれませんが、「待機が必要な場所には await を置く」というルールさえ覚えれば大丈夫です。
パソコンを触ったことがない方でも、まずはこの「型」に沿ってコードを書いてみることから始めてみましょう。自分の書いたプログラムが「PASS(合格)」と緑色で表示される瞬間は、パズルが解けた時のような達成感がありますよ。
TypeScriptと最新のJavaScript構文をマスターして、信頼性の高い、壊れにくいアプリケーションを作れるようになりましょう。非同期処理のテストは、そのための最も強力な武器になります。