コンテンツにスキップ

ホワイトボックステスト

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. ホワイトボックステスト

ホワイトボックステストとは

ブラックボックステストと組み合わせて単体テストを構成する。

実装内容が適切かを確認しながら、入力に対して、適切な出力が行われているかを検証する。

testing_whitebox-test


ホワイトボックステストの種類

ブラックボックステストと同じ名前のテストがあるが、実装内容を気にするか否かという点で、テスト内容は異なる。

  • 整形
  • 静的解析 (例:文法の誤りテスト 、ベストプラクティス違反テスト 、脆弱性診断、など)
  • ドライラン
  • 単体テスト
  • 機能テスト
  • 回帰テスト
  • 結合テスト


02. 静的解析

静的解析とは

▼ 文法の誤りテスト

ソースコードの実行前に、字句解析、構文解析、型解析、を行い、ソースコードの文法上の問題を検証する。

ソースコードの機械語翻訳の仕組みの違いのために、コンパイラ方式言語の場合はアプリケーションの実行前に文法の誤りを検出できるが、インタプリタ方式言語は実行時でしか検出できない。

そのため、特にインタプリタ方式言語では実施した方が良い。

▼ ベストプラクティス違反テスト

ソースコードのベストプラクティス違反を検証する。

言語によって、ビルトインのコマンド (例:go vetコマンド) で実現できる場合や、外部ツールが必要な場合がある。

▼ 脆弱性診断

ソースコードの実装方法に起因する脆弱性を検証する。

▼ その他

テストされるソースコード (例:Dockerfile、Kubernetesのマニフェスト、Terraform) によって、静的解析には他にも種類がある。


03. 単体テスト (ユニットテスト)

単体テストとは

クラスや構造体のメソッドが、それ単体で正しく動作するかを検証する。

言語によって、ビルトインのコマンド (例:go testコマンド) で実現できる場合や、外部テストツールが必要な場合がある。

検証対象以外の処理はスタブとして定義する。理想としては、アーキテクチャの層ごとに単体テストを実施する必要がある。

この時、データアクセスに関わる層の単体テストのために、本来のDBとは別に、あらかじめテスト用DBを用意した方が良い。

テスト用DBをdocker-compose.ymlファイルによって用意する方法については、以下のリンクを参考にせよ。


設計規約

▼ 単体テストの構成

単体テストはテストスイート (テストの組) から構成され、テストスイートはテストケース (テスト関数) に分類できる。

例えば、Goでは構造体をテストスイートとし、単体テストを定義する。

test-plan_test-suite_test-case

▼ テストケース名

Roy Osherove氏の命名規則に従って、『テスト対象のメソッド名』『入力値』『期待される返却値』の三要素でテスト関数を命名する。

期待される返却値の命名で『正常系テスト』か『異常系テスト』かと識別する。

例えば、正常系であれば『testFoo_Xxx_ReturnXxx』、また異常系であれば『testFoo_Xxx_ExceptionThrown』や『testFoo_Xxx_ErrorThrown』とする。

Roy Osherove氏の命名規則については、以下のリンクを参考にせよ。

▼ アサーションの比較値

単体テストのアサーションメソッドで、期待値と実際値を比較する場合、期待値を定数として管理した方が良い。


03-02. テストダブル

テストダブル

単体テストでは、各コンポーネントの依存対象のコンポーネントをテストダブル (代替品) に置き換える。


テストダブルの種類

▼ モック

上層クラス (上層構造体) が下層クラスを正しくコールできるか否かを検証したい時に、上層クラス以外の部分的処理は不要であり、下層クラスの実体があるかのように見せかける。

この時、見せかけの下層クラスとして使用する擬似オブジェクトを『モック』という。

スタブと用途が異なるが、モックもスタブも擬似オブジェクトである。

モックでは、クラスのメソッドとデータが全てダミー実装に置き換わる。

もし下層クラスを正しい回数実行できているかを検証したい場合は、下層クラスのモックを定義し、実体のある上層クラスが下層クラスにパラメーターを渡した時のコール回数と指定回数を比較する。

注意点として、用語の定義はテストフレームワークごとにやや異なることに注意する。

PHPUnitにおけるモックについては、以下のリンクを参考にせよ。

ツールの種類 モックのメソッドの返却値 補足
PHPUnit メソッドは、nullを返却する。 注意点として、finalprivateなメソッドはモック化されず、実体をそのまま引き継ぐ。また、staticなメソッドはBadMethodCallExceptionをスローするモックに置き換わる。
JUnit メソッドは、元のオブジェクトのメソッドの返却値の型を基に、初期値を返却する
(例:boolean型ならfalse)

▼ スタブ

クラスのメソッドの処理を検証したい時に、クラスが依存している下層クラスは、実体があるかのように見せかける。

この時、見せかけの下層クラスとして使用する擬似オブジェクトを『スタブ』という。

モックと用途が異なるが、モックもスタブも擬似オブジェクトである。

モックと同様にスタブでも、クラスのメソッドとデータが全てダミー実装に置き換わる。

スタブには、正しい処理を実行するように引数と返却値を持つメソッドを定義し、その他の実体のある処理が正しく実行されるかを検証する。

これらにより、検証対象の処理のみが実体であっても、一連の処理を実行できる。

注意点として、用語の定義はテストフレームワークごとにやや異なることに注意する。

PHPUnitにおけるスタブについては、以下のリンクを参考にせよ。


モックツール、スタブツール

  • PHPUnit
  • Phake
  • Mockery
  • JUnit


03-03. 網羅率

網羅率とは

網羅条件に基づいた単体テストの品質指標である。

採用した網羅で考えられる全ての条件のうち、テストで検証できている割合で表す。

網羅率はテストスイートやパッケージを単位として解析され、これは言語別に異なる。

言語やツールごとに網羅率を解析する方法が異なり、PHPのPHPUnitでは以下のリンクを参考にせよ。


網羅条件の種類

▼ C0:Statement Coverage (命令網羅)

p494-1

全ての命令が実行されるかを検証する。

*例*

AとBは、『1』または『0』になり得るとする。

条件 処理実行の有無
A = 1、B = 1 return Xが実行されること。

▼ C1:Decision Coverage (判定条件網羅/分岐網羅)

p494-2

全ての判定が実行されるかを検証する。

*例*

AとBは、『1』または『0』になり得るとする。

条件 処理実行の有無
A = 1、B = 1 return Xが実行されること。
A = 1、B = 0 return Xが実行されないこと。

▼ C2:Condition Coverage (条件網羅)

p494-3

各条件が、取り得る全ての値で実行されるかを検証する。

*例*

AとBは、『1』または『0』になり得るとする。

条件 処理実行の有無
A = 1、B = 0 return Xが実行されないこと。
A = 0、B = 1 return Xが実行されないこと。

または、次の組み合わせでも良い。

条件 処理実行の有無
A = 1、B = 1 return Xが実行されること。
A = 0、B = 0 return Xが実行されないこと。

▼ MCC:Multiple Condition Coverage (複数条件網羅)

p494-4

各条件が、取り得る全ての値で、かつ全ての組み合わせが実行されるかを検証する。

一般的に、複数条件網羅を採用すれば、最低限のソフトウェア品質を担保できていると言える。

*例*

AとBは、『1』または『0』になり得るとする。

条件 処理実行の有無
A = 1、B = 1 return Xが実行されること。
A = 1、B = 0 return Xが実行されないこと。
A = 0、B = 1 return Xが実行されないこと。
A = 0、B = 0 return Xが実行されないこと。


03-04. 循環的複雑度

循環的複雑度とは

コードの複雑さの程度のこと。

単体テストの品質の指標になる。

おおよそ判定条件網羅の経路数の程度である。


循環複雑度の種類

循環的複雑度 複雑さの状態 バグ混入率
10以下 非常に良い 25%
30以上 構造的なリスクあり 40%
50以上 テストできない 70%
75以上 変更によって誤修正が生じる。 98%


04. 機能テスト (フィーチャーテスト)

機能テストとは

アプリケーションの各エンドポイントを1個の機能ととらえる。

アプリケーション (またはダウンストリームのAPI Gateway) のエンドポイントにリクエストを送信し、外部Webサービスとの連携も含めて、レスポンスが機能要件通りに返信されるか否かを検証する。

スタブを使用することは少ない。

ブラックボックステストの機能テストとは意味合いが異なることに注意する。


05. 回帰テスト

回帰テストとは

テストの期待値ファイルを作成しておき、何らかの機能追加/変更によって、機能追加/変更を含むコンポーネントが既存のコンポーネントに影響を与えていないか (既存の機能がデグレーションしていないか) を検証する。

特にGoでは、このテストデータをファイルを『ゴールデンファイル』という。

ゴールデン (金) は化学的に安定した物質であることに由来しており、『安定したプロダクト』とかけている。


06. E2Eテスト (ホワイトボックステストの結合テスト)

E2Eテストとは

ホワイトボックステストでの結合テストは、特に『E2Eテスト』ともいう。

実際のユーザーの一連の操作を模したリクエストをアプリケーションに送信し、全てのコンポーネントを対象とした結合テストを実施する。


E2Eテストツール例

▼ 自前

言語によっては、ビルトインのコマンド (例:go testコマンド) でE2Eテストを実装できる。

各コンポーネントに対して実際のユーザーの一連の操作を模したリクエストを送信する。

▼ フロントエンド系ツール

フロントエンドに対して実際のユーザーの一連の操作を模したリクエストを送信する。

ツールは以下の通り。

  • curlコマンド
  • Cypress
  • Selenium
  • Puppeteer
  • TestCafe

▼ バックエンド系ツール

バックエンド (API) に対して実際のユーザーの一連の操作を模したリクエストを送信する。

ツールは以下の通り。

  • curlコマンド
  • Postman
  • Karate