Javaでテストを初めて学ぶなら、最初の1冊は『ソフトウェアテストの教科書[増補改訂 第2版]』です。「何を入力して、何が返れば正しいのか」を決めるために選びます。
単体テストの考え方/使い方変更に耐えられるテストの判断を深める。今すぐ購入は不要。学ぶこと・得たい力を見る ↓Javaの文法を使い、処理をクラスに分けられるようになったら、次はその処理を自分で確かめる段階です。「JUnitのテストが緑になった」から一歩進んで、なぜそのケースを選んだのかまで説明できるようになりましょう。
2冊目の『単体テストの考え方/使い方』は、テストを書いた経験ができてからの候補です。最初に2冊とも買う必要はありません。以下の短いコードを見て、ケースを選べないのか、書いたテストの守り方に迷うのかを分けます。
この記事でわかること
- テスト入門で最初に選ぶ1冊と、その本が必要な理由
- 送料のコードから、ケースの不足と期待値の決め方を考える方法
- 本で学んだ判断をJUnitに移し、Springへ進む目安
目指すのは、仕様からテストを選び、変更後も確かめられる人
学習の目標は、本の用語やJUnitの機能を丸ごと暗記することではありません。小さなJavaの処理に対して、入力・期待する結果・そのケースを選んだ理由をそろえて、繰り返し実行できることです。
私は、コードの理解を確かめるときに「今回の要件」「取れる選択肢」「その手段を採用した理由」「他の手段より適する理由」を重視しています。テストにも同じ問いを当てはめられます。思いついた数値を渡しただけでは、何を守るテストなのかが引き継げません。
たとえば、送料を決める処理の確認なら、手で画面を操作する方法と、Javaのメソッドを直接呼ぶ方法があります。今回は入力と戻り値だけで料金の約束を確かめられるため、JUnitで直接呼びます。画面の見た目や操作は、このテストでは確認できません。何を確認するために選んだ方法かを言えることが大切です。
この送料のコードなら、何円を入力して確かめますか?
商品小計から送料を求める、学習用の例です。仕様は次の3つとします。小計0円を受け付けることも、この例で決めた約束です。
- 小計は整数の円で、0円から100万円までを受け付ける。
- 小計が10,000円以上なら送料無料。それ以外は500円。
- 範囲外の小計を渡したら、
IllegalArgumentExceptionにする。
public int calculate(int subtotal) {
if (subtotal < 0 || subtotal > 1_000_000) {
throw new IllegalArgumentException("小計は0円以上100万円以下です");
}
return subtotal >= 10_000 ? 0 : 500;
}calculateが返すのは、商品小計を含めない送料だけです。上のコードはこの仕様どおりに動きます。ここでは、書き間違いを探すのではなく、仕様を確かめるケースを選んでみてください。
最初に次のテストを1件書いたとします。6,000円を渡し、送料500円が返ることを自動で確かめています。
@Test
void 小計6000円の送料は500円() {
ShippingFee shippingFee = new ShippingFee();
assertEquals(500, shippingFee.calculate(6_000));
}@TestはJUnitに実行対象だと伝える印です。assertEquals(期待値, 実際の値)は2つを比較し、違えば失敗にします。500を先に置いているのは、仕様から決めた送料が500円だからです。
ここで迷ったら、最初の1冊へ
6,000円の1件に、何を追加すればよいかわからない
10,000円の扱い、入力できる金額の下限と上限。そのどれを、どんな値で確認するかを決める力が必要です。
『ソフトウェアテストの教科書』で学ぶことを見る →テストを書いた経験ができたら
結果は同じなのに、処理を整理するたびテストが壊れる
戻り値を守りたいのか、内部メソッドの呼び方を固定しているのか。テストが何に依存しているかを考える段階です。
『単体テストの考え方/使い方』で学ぶことを見る →「newや引数・戻り値の意味からまだ難しい」場合は、先にJava基礎を補ってください。一方、JUnitの書き方を知らなかっただけなら、この短い例を動かしながら進められます。テストの本を買う前に、構文の不明点とケースを選ぶ判断の不明点を分けておきましょう。
『ソフトウェアテストの教科書』で、入力と期待値を選ぶ理由を学ぶ
「正常な値を1件試した後、何を足せばよいかわからない」人が最初に選ぶ本です。紹介しているのは増補改訂 第2版です。テストの基本から、同値分割・境界値、条件の組合せ、状態の変化を整理する考え方を学べます。
ソフトウェアテストの教科書[増補改訂 第2版]
仕様から、試す入力と期待する結果を選べるようになる。
同値分割・境界値や条件の組合せなど、テストケースを選ぶ考え方を基礎から学ぶ1冊。何を試せばよいかわからない段階で選びます。
- 正常な値を1件試した後、追加するケースに迷う
- 入力と期待値を選んだ理由を説明したい
当サイトはAmazonアソシエイト・プログラムの参加者です。価格・在庫・配送条件はAmazonでご確認ください。
ここで学ぶこと:同じ結果になる範囲と、結果が切り替わる場所
送料の例では、受け付ける金額を「0〜9,999円」と「10,000〜1,000,000円」に分けられます。前者の送料は500円、後者は0円です。同じ扱いになる入力をまとめ、代表を選ぶ考え方が同値分割です。6,000円は前者の代表にはなりますが、送料無料の側はまだ確かめていません。
さらに、10,000円ちょうどは送料が変わる境目です。「以上」を「より大きい」と取り違えた実装は、6,000円でも20,000円でも発見できません。そこで9,999円・10,000円・10,001円を選び、境界の前後と境界そのものを確認します。

ここで必要なのは「境界値」という言葉を言えることより、どの仕様の境界なのかを指せることです。この例には送料無料の境界に加え、入力として受け付ける金額の境界もあります。下限の周辺は−1円・0円・1円、上限の周辺は999,999円・1,000,000円・1,000,001円です。
値を無闇に増やすより、「送料の切替」「入力できる下限」「入力できる上限」という3つの理由を先に分けると、確認の抜けを説明できます。どの値も必要だから追加するのであって、9件という件数に意味があるわけではありません。
ここで得ること:「正しく動く」の中身を他の人へ渡せる
レビューで「送料無料の条件は確認しましたか」と聞かれたときに、「JUnitが通りました」だけで終えず、「10,000円以上という仕様に対して、直前・ちょうど・直後を確認しました」と答えられることが目標です。これなら、レビューする人も何が未確認かを判断できます。
業務システムの金額条件は、後から担当者が変わっても読み直されます。期待値に理由があれば、担当者が「10,000円の送料はなぜ0円なのか」をコードの動きから推測し直す必要が減ります。テスト名にも、入力の条件と期待する結果を残しましょう。
本で扱う条件の組合せは、たとえば将来「会員なら小計に関係なく送料無料」と決まったときに役立ちます。金額の境界だけを増やすのではなく、会員かどうかと金額条件が組み合わさるからです。今のサンプルは金額だけに絞り、条件が増えたときに新しい考え方を持ち出します。
次に埋めること:選んだケースをJUnitで繰り返し実行する
この本は、JUnitの設定やAPIだけを順に覚える本としては選んでいません。本で学ぶのは、何を確認するかを決める考え方です。Javaでどう実行するかは、この記事のサンプルにケースを追加してつなげてください。
今の例なら、ケースを紙に書いた段階で「入力10,000円、期待する送料0円」と決められます。実行して返ってきた500円を、そのまま期待値にしてはいけません。実装が間違っていたとき、間違った結果まで正しいことにしてしまうためです。仕様が曖昧なら、テストを書く前にその仕様を確認します。
選んだケースをJUnitに書き、変更後にまとめて実行する
送料の境界を確かめる3件を、JUnitでは次のように書けます。今回はshippingFeeをテストクラスのフィールドに置き、各テストメソッドから使います。先ほどの1件ではメソッドの中で生成していた点との違いです。読めることを優先し、最初は1件ずつ分けています。クラス宣言とimportを含む完全なコードは、後のサンプルに入っています。
private final ShippingFee shippingFee = new ShippingFee();
@Test
void 小計9999円の送料は500円() {
assertEquals(500, shippingFee.calculate(9_999));
}
@Test
void 小計10000円の送料は0円() {
assertEquals(0, shippingFee.calculate(10_000));
}
@Test
void 小計10001円の送料は0円() {
assertEquals(0, shippingFee.calculate(10_001));
}テスト側にsubtotal >= 10_000 ? 0 : 500をもう一度書く必要はありません。仕様から決めた0円・500円と、本体が返した値を比較します。本体の条件式をコピーすると、同じ勘違いを両方へ写しても成功してしまいます。
範囲外を拒否する約束は、例外を期待する形にします。ここでassertEqualsに送料を渡すのではなくassertThrowsを使うのは、仕様が「何円を返すか」ではなく「受け付けない」だからです。
@Test
void 負の小計は受け付けない() {
assertThrows(IllegalArgumentException.class,
() -> shippingFee.calculate(-1));
}() ->の右側は、JUnitに実行してもらう処理です。calculate(-1)が指定した例外、またはその派生型を出せば成功します。例外が出なかったり、それ以外の例外だったりすれば失敗します。
失敗したら、期待値を実際の値へ合わせる前に仕様を読む
本体の>= 10_000を一時的に> 10_000へ変えると、10,000円のケースが失敗します。これは「送料0円を期待したが、実際は500円だった」という意味です。期待値を500円に書き換えれば成功しますが、10,000円以上は無料という約束は破ったままです。
まず仕様が変わったのかを確認し、今回は変わっていないので本体を戻します。テストが失敗したこと自体を消すのではなく、仕様・期待値・実装のどれが食い違っているかを調べます。これが、失敗の表示を見て次の作業を判断する練習になります。
仕様変更では、新しいケースと、変わらない約束の両方を確認する
今度は、仕様そのものが「8,000円以上は送料無料」に変わったとします。7,999円・8,000円・8,001円のケースを追加し、送料500円・0円・0円を期待します。先に実行すると変更前の本体では8,000円と8,001円が失敗します。そこで本体を変更し、全件を実行します。
以前の9,999円のケースは、新仕様では送料0円に変わります。こうした期待値は仕様に合わせて更新します。一方、負の小計や100万円超を拒否する約束は変わっていません。そのケースも一緒に実行し、別の約束を壊していないかを確認します。
『単体テストの考え方/使い方』は、テストを直す負担に迷ってから読む
ケースを選んでテストを書けるようになり、「本体を整理すると大量のテストまで直す」「テストは増えたのに変更が怖い」と感じたら、次の候補が『単体テストの考え方/使い方』です。最初の本と役割が違い、残して育てる価値のあるテストを考えるために選びます。
単体テストの考え方/使い方
単体テストで「何を検証するか」の判断基準を学ぶ。
モックの使いどころや壊れにくいテストの考え方を整理できます。コード例はC#です。JUnitの操作入門ではなく、テストの設計と価値を深める本です。
- テスト対象の切り分けに迷っている
- 保守しやすい単体テストを書きたい
当サイトはAmazonアソシエイト・プログラムの参加者です。価格・在庫・配送条件はAmazonでご確認ください。
この本で学ぶ対象は、良いテストの性質、実装の整理に耐えられる確認、内部の手順へ依存しすぎない考え方などです。掲載コードはC#です。Javaで最初のJUnitテストを動かす手順を求めている段階では、まず前のサンプルを使ってください。
送料の例で、内部にisFreeShippingというメソッドを切り出したとしても、同じ小計から同じ送料を返すなら、今のテストは書き換えずに実行できます。呼び出し元から見える約束を確認しているからです。反対に、内部メソッドの名前や呼出順まで無条件に固定すると、結果を変えない整理でもテストが失敗することがあります。
ここで得たいのは、テストの成功件数を増やすこと以上に、「何が変わったとき、このテストには失敗してほしいか」を説明する力です。送料の約束が崩れたら失敗してほしい。一方、約束を保ったまま内部を読みやすくしただけなら、同じ確認を使い続けたい。この区別が本の内容を自分のコードへ当てはめる入口です。
ただし、どんな場面でも戻り値だけ見れば十分という意味ではありません。結果として何を守るのかは、対象の仕様によって変わります。まず料金のような小さな処理で判断の基準を持ち、扱う仕事が広がったときに本へ戻る使い方ができます。2冊目を読了することを、次の学習へ進む条件にはしません。
Springへ進む目安は、選んだケースを自分で説明し、実行できること
次の項目を、この送料の処理を使って確認してください。書き方を調べながらでもかまいません。大切なのは、自分が何を確認しているのかを説明できることです。
1〜3で止まるなら、最初の本でケースの選び方を補います。4〜5で止まるなら、サンプルを実行し、1件ずつ入力と期待値を変えてみてください。「本をもう1冊買う」ことと「実際にテストを動かす」ことを、同じ不足として扱わないようにします。
確認できたら、Springで画面・Javaの処理・保存をつなぐ学習へ進めます。今回JUnitで守れたのは送料の計算です。Webアプリへ組み込んだ後は、画面から値が届くか、処理の結果を表示できるかという確認が増えます。小さな計算を先に自動で確かめられると、問題が計算にあるのか、そのつなぎ方にあるのかを分けやすくなります。
最初は1冊で、テストケースを選ぶ理由を身につける
Javaのテスト入門で最初に勧めるのは、『ソフトウェアテストの教科書[増補改訂 第2版]』です。正常な入力を1件試した後に、なぜ境界や範囲外も確かめるのか。その判断を、自分の処理へ当てはめるために使ってください。
ケースを選べるようになったら、JUnitで入力・期待値・実行結果を結び付けます。『単体テストの考え方/使い方』は、その後、変更に耐えられるテストを育てるための1冊です。まずは、10,000円の送料をなぜ0円と確かめるのかを、自分の言葉で説明するところから始めましょう。


