ユニットテストとは
アプリは一つの大きなプログラムに見えますが、実はたくさんの小さな部品(関数やクラス)が組み合わさってできています。ユニットテストは、その部品を一つずつ取り出して「ちゃんと動くか」を確かめるための、小さなプログラムです。
この章では、ネットショップの「割引を計算する部品」を例に、ユニットテストが何を確かめているのか、どう書くのか、そしてなぜ役に立つのかを、図を動かしながら見ていきます。
アプリを小さな部品に分けて、1つずつ確かめるユニットテストの基本。入力と期待する答え、Arrange→Act→Assertの書き方、モックによる切り離し、そしてユニットテストだけでは見つからない問題まで、図が動く形で解説します。
アプリは一つの大きなプログラムに見えますが、実はたくさんの小さな部品(関数やクラス)が組み合わさってできています。ユニットテストは、その部品を一つずつ取り出して「ちゃんと動くか」を確かめるための、小さなプログラムです。
この章では、ネットショップの「割引を計算する部品」を例に、ユニットテストが何を確かめているのか、どう書くのか、そしてなぜ役に立つのかを、図を動かしながら見ていきます。
ネットショップのアプリを分解してみると、合計金額を計算する部品、割引を計算する部品、税を計算する部品、在庫を確認する部品……というように、役割ごとの部品に分かれています。コードの上では、これらはたいてい calcTotal() のような関数や、クラスとして書かれています。
アプリ全体をいきなり確かめようとすると、おかしな動きをしたときに、どこが悪いのか分かりにくくなります。そこで、まずは部品の単位で確かめよう、というのがユニットテストの考え方です。
「ユニット(unit)」は英語で「単位」という意味です。ユニットテストでは、アプリの中から部品を一つだけ取り出して、それ単体で正しく動くかを確かめます。
ここでは「割引を計算する部品」、つまり calcDiscount(price, rate) という関数を取り出しました。値段と割引率を渡すと、割引後の金額を返してくれる関数です。ほかの部品のことはいったん忘れて、この関数だけに集中するのがポイントです。
ユニットテストがやっていることは、とてもシンプルです。部品に入力を渡し、返ってきた答えが、あらかじめ決めておいた「期待する答え」と同じかどうかを比べるだけです。
たとえば「1000円の商品に10%の割引をかけたら、900円になるはず」と決めておきます。実際に関数を動かして900円が返ってくれば合格(緑)、違う金額が返ってくれば不合格(赤)です。人間が電卓で確かめていた作業を、プログラムに代わりにやってもらうイメージです。
ユニットテストは、たいてい3つの段階で書きます。まず Arrange(準備) で、テストに使う値をそろえます。次に Act(実行) で、調べたい関数を1回だけ呼び出します。最後に Assert(確認) で、返ってきた結果が期待どおりかを確かめます。
前のページの「入力」「部品」「答え」が、そのままこの3段に入れ替わったのが分かるでしょうか。expect(result).toBe(900) は「resultは900であるはずだ」と読みます。どんなに複雑なテストでも、この型に当てはめると読みやすくなります。
同じようにして、合計の計算、税の計算、在庫の確認……と、部品ごとにユニットテストを書いておきます。右上の緑の丸は、その部品のテストが全部合格していることを表しています。
ユニットテストは一つあたり数ミリ秒で終わるので、何百個あっても数秒で全部を実行できます。コードを少し変えるたびに全部流しても、ほとんど苦になりません。
ある日、誰かが割引の計算式をうっかり書き間違えたとします。テストを実行すると、割引を計算する部品のテストだけが赤くなりました。ほかの部品は緑のままです。
これがユニットテストのいちばんうれしいところです。「どこかがおかしい」ではなく「この部品のこのケースがおかしい」と、壊れた場所をピンポイントで教えてくれます。だから、コードを直したり整理したりするのが怖くなくなります。
実際の部品は、データベースや外部のサービスなど「外の世界」に問い合わせながら動くことがよくあります。たとえば割引の部品が、クーポンコードの割引率をデータベースに聞きに行く場合です。
このままテストすると、データベースへの接続に時間がかかったり、誰かがデータを書き換えたせいで結果が変わったり、ネットワークがないと動かなかったりします。部品そのものは正しいのに、テストが遅く、不安定になってしまうのです。
そこでユニットテストでは、外の世界の部分を「ニセモノ」に差し替えます。これをモック(mock)と呼びます。モックは「SAVE10と聞かれたら10%と答える」のように、決まった答えだけを返す簡単な代役です。
こうすると、テストは一瞬で終わり、何度実行しても同じ結果になります。確かめたいのはあくまで「割引の計算が正しいか」なので、データベースが本物かどうかは、この段階では関係ないのです。
ユニットテストには弱点もあります。割引の部品は「割引額(100円)」を返すつもりで作られ、決済の部品は「割引後の支払額(900円)」が渡ってくるつもりで作られていたとします。それぞれのユニットテストは、どちらも合格します。
ところが二つをつなぐと、決済の部品は100円を支払額だと思って処理してしまいます。部品ひとつひとつは正しくても、つなぎ目の「約束」がずれていると壊れるのです。この問題を見つけるのが、次の章で紹介する統合テストです。
ログインするとコメントできます。
読み込み中…
権利侵害・不適切な内容の連絡: [email protected]