コンテンツにスキップ

ホワイトボックステスト

はじめに

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


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) のエンドポイントにリクエストを送信し、外部APIとの連携も含めて、レスポンスが機能要件通りに返信されるか否かを検証する。

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

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


05. 回帰テスト

回帰テストとは

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

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

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


06. E2Eテスト

E2Eテストとは

実際のユーザーを模した一連の操作 (フロントエンドへのリクエスト) を実施し、特定の機能に関する全てのコンポーネント間 (フロントエンド、バックエンド、外部APIなど) の連携をテストを実施する。

注意点として、特定のコンポーネント間の連携をテストする『結合テスト』の一種ではない。


E2Eテストツール例

▼ 手動

手動でフロントエンドを操作し、E2Eテストを実施する。

▼ 自前

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

実際のユーザーを模した一連の操作 (フロントエンドへのリクエスト) を実施する。

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

実際のユーザーを模した一連の操作 (フロントエンドへのリクエスト) を実施する。

  • Autify
  • Cypress
  • Mabl
  • Selenium
  • Puppeteer
  • TestCafe

▼ バックエンド系ツール

実際のユーザーを模した一連の操作 (APIへのリクエスト) を実施する。

ツールは以下の通り。

  • curlコマンド
  • Postman
  • Karate