単体テストのやり方!手法や観点、仕様書についてわかりやすく解説
2023.11.18

開発の現場において、設計と並んで大事なことが「テスト」です。
その中でも、ソフトウェアの品質を担保するうえで非常に重要な「単体テスト」について解説します。


1.システム開発の流れ

はじめに、システム開発における全体の流れから確認していきましょう。

現在、システム開発の多くは「ウォーターフォール型」での開発が多く採用されています。


まずは、ウォーターフォールでの開発の流れを押さえておきましょう。

ウォーターフォール型の開発では、以下のように、設計からスタートし、コーディングを行ったうえでテストを実施します。

ウォーターフォール型開発のイメージ図



2.単体テスト(ユニットテスト)とは?


単体テストは、「プログラムが単体で動作するかを確認する」ことを目的として行うテストです。

まずは、「単体テストとは何か」についてしっかりと押さえていきましょう。

①単体テストの目的

単体テストの目的は、「プログラム・モジュールが単体で正常に動作すること」を確認することです。


プログラムが単体で正常に動作することを保証することで、以降に実施するテストをスムーズに進めることが可能になります。

②その他テストとの違い

単体テストは、数あるテストの中でも最小単位となるテストです。
単体テストが終了すると、次は以下のようなテストを実施していきます。


特にウォーターフォール型の開発においては、対応する設計フェーズに合わせたテストを実施するので、あわせて覚えておくとよいでしょう。

結合テスト

結合テストは、複数のプログラムを実際に「結合」させてテストを行います。


テストの内容は、「基本設計」に対応する形でテストを実施します。
例えばJavaのWebアプリであれば、実際にデータベースに接続して画面を表示するまでをテストとして実施するのです。

システムテスト

システムテストは、結合テストよりもさらに広い視点で行うテストです。


開発したシステムが全体として正常に動作するのかに着目し、「要件定義」に対応するテストを実施します。


実際に運用を加味してシナリオを作成してテストをする「シナリオテスト」や、大量にアクセスがあった場合、高負荷の状態でも正常に動作するかをテストする「負荷テスト」を行います。

運用テスト

運用テストは、実際に運用する環境下で行うテストです。


システムテストまでは開発側がテストを実施していたのに対し、運用テストは発注側が主体となってテストを進めます。

運用テストでは発注側が主体となって、「発注したシステムが正しく開発されたのか」に着目します。


そのため、システムテストよりもさらに実運用に沿ったテストが実施されることになるので、それまでの視点とは異なった目線でテストが行われることに注意しましょう。



3.単体テストの仕組み


単体テストは「プログラム単体での動作を保証する」という目的で実施しますが、実際の業務においては単一のプログラムで構成されるシステムは存在しないといっても過言ではないでしょう。


ほとんどの場合、複数のプログラムからひとつのシステムを構成します。


では、他のプログラムを利用している場合や他のプログラムから利用される前提のプログラムは、どうやってテストをするのでしょうか?


そこで利用するのが「ドライバー」と「スタブ」です。

単体テストでは、このドライバーとスタブを活用することで、効率よくテストを行っていきます。

①ドライバー

ドライバーは、テスト対象を動かす(ドライブ)するために作成するプログラムです。

他のプログラムから利用されるプログラムをテストする場合、このドライバーを用いてテスト対象のプログラムに引数を与えます。

②スタブ

スタブは、テスト対象が呼び出す他のプログラムの動作を代替するために作成する仮のプログラムです。


本来であれば外部プログラムを呼び出す部分を、呼び出し先をスタブに切り替えてテストを実施します。

スタブが仮となる処理を実施することで、その外部プログラムが完成していなくてもテスト対象をテストすることが可能となるのです。

スタブの使われ方




4.単体テストの種類と観点


ここからは、単体テストを行ううえでの手法とその観点を紹介します。


主に、単体テストは「ホワイトボックステスト」と「ブラックボックステスト」のふたつの視点で行うことになります。

①ホワイトボックステスト

ホワイトボックステストは、その名の通りテスト対象を「ホワイトボックス」、つまり「ソースコードの中身が見える」前提でテストを行います。


実際のソースコードのif文やfor文、while文といった条件を網羅できるようにテストを行います。

条件網羅

ホワイトボックステストの観点として、「条件網羅」という考え方があります。
条件網羅は、「条件式の全パターンを網羅しているか」という点に着目してテストケースを設定します。


例として、酒類を購入できる条件を判定するプログラムを考えてみましょう。

酒類を購入できる条件は以下の2点です。


・20歳以上であること

・身分証明を提示すること

それをもとに以下のプログラムを作成しました。

/**
 * 酒類が購入可能か
 * @param age 年齢
 * @param hasIDCard 身分証明書保持
 * @return 購入可能であればtrueを返却
 */
public static boolean ableToBuy(int age, boolean hasIDCard) {
    if (age >= 20 && hasIDCard) {
        return true;
    } else {
    return false;
    }
}

このとき、if文にはふたつの条件が記載されています。そのため、このプログラムをテストするためには、以下の条件を確認する必要があるということです。

パターンagehasIDCard戻り値
(1)20以上truetrue
(2)20以上falsefalse
(3)20未満truefalse
(4)20未満falsefalse

このように、ソースコードの条件に応じてテストケースを設定することを「条件網羅」と呼びます。

②ブラックボックステスト

対して、ブラックボックステストはテスト対象を「ブラックボックス」、つまり「ソースコードの中身が見えない・わからない」という前提でテストを行います。


具体的には、メソッドの引数や戻り値、クラスのプロパティ値に着眼点を置きテストを実施します。


例として、年齢によって条件が変わるテストを考えてみましょう。

今回は、未成年(18歳未満)とシニア(65歳以上)は利用料が割引されるような場合でテストを設定してみます。

同値分割

条件に合致する条件を考慮すると、年齢層を以下のように分けられます。

分割項目年齢
未成年0歳~17歳
成年18歳~64歳
シニア65歳以上

同値分割は、「同じ条件として扱える値」に着目してテスト内容を決定する手法です。
条件を考慮することで、「未成年」、「成年」そして「シニア」の3パターンをテストすればよいことがわかります。


そのため、今回の場合は「9歳」、「40歳」、「70歳」のようなテストをすれば十分ということです。

境界値分析

数値を条件としたプログラムを書くときに、気を付けないといけないのが「以下」と「未満」、そして「以上」と「超過」です。


例えば、整数値を比較する場合、プログラム上では以下のif文は同じ結果になります。

// 17以下
if (age <= 17) { ~ }

// 18未満
if (age < 18) { ~ }

境界値分析は、条件の境界をもとにテストする値を決定する方式です。

境界値分析の結果、以下の項目をテストする必要があることがわかります。


未成年と成年の境界:「17歳」と「18歳」
成年とシニアの境界:「64歳」と「65歳」

条件設定の考え方はホワイトボックステストの「条件網羅」と似た内容になり、同じようなテストを実施することになります。


境界値分析は、あくまでも処理内容ではなく「プログラムの仕様」に着目している点に注意しましょう。

複雑な条件式を持つ場合こそ、それぞれの観点でテストを実施することでバグを発見しやすくなります。

正常値と異常値

テストする値に対して正常な値と不正な値を与えてテストを実施します。

例えば、年齢の場合であれば正の整数のみが対象となるため、文字列や記号はもちろんのこと、小数点や負の値も指定できないはずです。


そのため、これらの予期しない値が入力された場合にもきちんと対処できているか確認する必要があります。


特に年齢の場合、整数値で受け付けるため小数点や文字列はありませんが、マイナスの値が入力された場合には予期しない挙動をする場合があるので、しっかりと確認することが大切です。



5.単体テストのやり方

次に、単体テストの流れを紹介します。

①仕様書を作成

まずは、テスト仕様書を作成します。
先ほど紹介したテストの観点に準じて、どのようなテストを実施するのかを考えていきましょう。


まず、Excelなどを利用して実施するテストの一覧を作成します。
そのうえで、そのテストをプログラムで実施するための「テストコード」を作成しましょう。


開発の現場によっては、テストコードのみを作成して仕様書とする場合もあります。

②単体テストを実施

仕様書をもとに、単体テストを実施します。
テストコードを作成している場合には、そのテストを実施することで実行結果を得られます。

③エビデンス採取

テストが終了した場合には、成功・失敗にかかわらずその実行結果を保存しましょう。その実行結果を「エビデンス」と呼びます。


多くの場合、以下のようなものがエビデンスの対象となります。


・テストの実行結果

・ログ

・ファイル(入出力がある場合)

開発の現場ごとに残すエビデンスの種類が決まっているので、実務で開発を行う場合には実施前に確認するようにしましょう。



6.単体テストのメリット


単体テストの観点や内容を理解したところで、単体テストを行うメリットを解説します。

①不具合の特定や修正が容易

単体テストは、プログラムのテストの中でも最小単位であるため、テストに失敗した場合にはそのプログラムに問題があることが一目でわかります。


そのため、単体テストで発生した問題は簡単に対処することが可能です。

②早い段階で不具合を発見できる

ソフトウェアの不具合は、後工程になるほど調査が難航しやすく時間がかかってしまうことが多いです。

ひとつのプログラムのバグが原因で、複数の障害が発生していたということもよくあります。


単体テストをしっかりと実施し、そのプログラムが正常に動作することを保証することで、後工程にある結合テストやシステムテストで発生するバグの件数を減らせます。



7.単体テストのデメリット


メリットの大きな単体テストですが、デメリットもあります。


とはいえ、単体テストを実施しないという選択肢はありません。
これらのデメリットを理解した上で効率よくテストを進めることを忘れないようにしましょう。

①工数がかかる

単体テストを実施する場合、どうしても時間(工数)がかかってしまいます。
実際問題として、テスト対象のコードを作成した時間と同じか、それ以上の時間を単体テストに費やすことになります。


そのため、単体テストの実施を嫌がる開発者も多いことでしょう。


しかしながら、単体テストを実施しないということは、プログラムの正常動作を保証できないということです。

単体テストをしっかりと実施しなかった結果、結合テストやシステムテストにおいて、多くの障害を発生させ大きな遅延や損害が発生したケースも数多く存在します。


そのため、開発する際には、デメリットをあらかじめ考慮したうえで進めるという工夫が必要になります。

②テスト実施者により単体テストの効果が異なる

前述のとおり、単体テストを実施するために必要な観点は多く、仕様書を作成するにはある程度の知識や経験が必要です。


特に、ブラックボックステストではプログラムの仕様を正しく理解していないとテストの意味がまったくなくなります。


つまり、効果的なテストを行うためには経験や知識が必須となるため、テストを実施する人によってプログラムの品質に影響が最も出やすいのが単体テストと言えます。

初心者が単体テストを実施する際には、しっかりとレビューをすることも忘れないようにしましょう。



8.単体テストの自動化


単体テストは、作成したプログラムに対してテストを作成するため、最終的にテストの数が膨大になりがちです。


単体テストを実施するとなると、それだけでかなりの時間がかかってしまいます。
かといって、実施するテストを減らすのは品質に問題が出てしまいます。



そのため、単体テストは自動でテストを実行する「テストフレームワーク」を利用するのが一般的です。

テストフレームワークを利用することで、膨大なテストも自動で実行してくれます。


単体テストを実施するためのフレームワークはプログラミング言語ごとに存在しています。
これらのテストフレームワークは、総称して「xUnit」と呼ばれているので、業務でプログラミングをする際にはぜひ覚えておきましょう。


プログラミング言語と、対応するテストフレームワークの一例を紹介します。

プログラミング言語テストフレームワーク
JavaJUnit
PHPPHPUnit
PythonPyUnit
C#xUnit.net

Java向けのテストフレームワークである「JUnit」の詳しい使い方や解説は、次の記事を参照してみてください。




この記事をシェア