開発者のためのソフトウェアテストのスキルアップ

2018年10月16日(火)
kyon_mm

はじめに

ここまで、さまざまなソフトウェアテストの考え方や種類を紹介してきました。開発者がソフトウェアテストを活用していくなかで、「どのように問題を分割してすすめて行けば良いのか」と「どのようなテストケースを選択するのか」という2つの課題は筆者に多く相談がきます。

今回は、この2つの課題に対して、どのような方法で自らのスキルを上げて行けば解決できるのかを解説します。具体的には、前者には「Mikadoメソッド」を、後者には「テスト技法」を活用します。

筆者がよく耳にするソフトウェアテストの課題

開発者がTDDやテスト設計に取り組む際、筆者はよく次のような課題を耳にします。

  1. どのように問題を分割するのか
  2. 問題に対してどのようなテストケースを選択するのか

「どのように問題を分割するのか」とは、TDDやテスト設計において「開発対象をどのような問題に分割してテストを作れば良いのかわからない」といった課題です。

TDDでは、テストを書く前に全体として何をやるべきかのTODOリストを作成します。いわゆるタスク分割です。この粒度や順番をどのようにすれば効率的なのかがわからず、いつも悩んでしまうというものです。Whatを決める作業といえます。

Whatが決まったところで、それに対してどんなテストケースを選択すれば、自分が考える十分な品質を保てると言えるのかがわからないといった課題も耳にします。これが「問題に対してどのようなテストケースを選択するのか」ということです。

テストのサイズが大きければ大きいほど難しくなる課題ですが、初心者にとっては小さいテストでも難しいです。これはHowやHow muchを決める作業といえます。

この2つの課題に対して、ここでは次の方法で解決をしていきます。

  1. どのように問題を分割するのか => Mikadoメソッド
  2. 問題に対してどのようなテストケースを選択するのか => テスト技法

Mikadoメソッドは、小さく安全なステップを繰り返して複雑なコードに重要な変更を加えるための構造化されたプロセスです。

テスト技法は、テストケースを作成したり選択したりするための技法です。テスト対象の構造をグラフ化してテストケースとして一定基準で網羅するような技法や、経験則や不具合情報から作成する技法などがあります。

Mikadoメソッドの詳細は「The Mikado Method」などを参考にしてください。書籍も刊行されています。また、テスト技法の詳細は『ソフトウェアテスト技法ドリル』(秋山浩一著:日科技連出版社)などを参考にしてください。

以降では、これらの基本を紹介します。

どのように問題を分割するのか => Mikadoメソッド

Mikadoメソッドは「コードに重要な変更を加えるためのプロセス」と説明しましたが、具体的には図1のようなフローチャートで構成されます。

図1:Mikadoメソッド自体のフロー

このフローチャートにしたがって作業をしていくなかで、タスクを図示していきます。この図示したものを「Mikadoグラフ」と呼びます(図2)。

図2:Mikadoグラフ

Mikadoメソッドではタスクの最終的なゴールを「Mikadoゴール」と呼び、それに必要な作業を「事前条件」と呼びます。MikadoグラフはMikadoゴールと事前条件で構成されます。最初は紙とペンを使って実施すると良いでしょう。

大まかにMikadoメソッドのプロセスを追ってみます。

  1. まず、達成したいMikadoゴール(例:追加したい機能や加えたい変更)を記述する
  2. そのゴールやもしくは着手対象の事前条件を簡素に実装する
  3. 2.の過程でコンパイルエラーやソフトウェアの実行エラーが出ているか確認する
  4. 3.の結果でエラーがあった場合は、それらを簡単に解決できそうな方法を探す
  5. 見つけた解決法をゴールに対する事前条件として追加する
  6. 2.で加えたコードへの変更をすべて戻し、何も変更していない状態にする
  7. 次に着手すべき事前条件を選択する。これを繰り返す。

  8. 3.の結果としてエラーがない場合には、変更によりゴールに辿りつくための振舞いとして変更が十分かを考えます。十分な変更ではない場合は次の事前条件に着手します。

  9. 変更によってゴールに辿りつくための振舞いとして変更が十分ならばコードをバージョン管理ツールにコミットする
  10. Mikadoゴールに辿りつけば完成。そうでなければ別の事前条件に着手する。これを繰り返す

このように、Mikadoメソッドは想定したことが上手くいかなかったときには新しく発見した事前条件(やるべきこと)を追加し、加えた変更をすべて戻して追加した事前条件やそれよりももっと前提になる事前条件から手を付けるというプロセスです。これにより、最終的にはやるべき順番でならんだタスクリスト(Mikadoグラフ)が手に入り、その順番で実施すればうまくいくはずです。

短時間でやるべきことを見つけることはできませんが、確実に必要なものを見つける手助けになります。

6.で試行錯誤して加えた変更を戻し、7.で別の事前条件を追加してやり直す作業には、Gitをはじめとするバージョン管理システムが非常に役立ちます。

簡単に言えば、6.では’git reset --hard’するということです。「不慣れなので、今の作業を残したい」という場合においても、別のブランチを切ってコミットし、元のブランチに戻りましょう。

Mikadoメソッドを実施するときには、分散バージョン管理システムを利用することで安全に進めることができます。

Mikadoメソッドで大切なのは、最初から解決策を練り込むのではなく、まずはすぐに解決できそうな簡素な方法を試してソフトウェアがどうなったかを観察し、次にやるべきことを考えていく点です。

このように、簡素ですぐに解決できそうな方法を考えていくことを、Mikadoメソッドでは「ナラティブ・アプローチ」と呼んでいます。手順で言うと2.や4.が該当します。

ナラティブ・アプローチで進めて行き、実施すべきタスクが洗い出せたらしっかりと方法を練ります。思い込みでタスクを作っていかないための方法論です。

図3~6のMikadoグラフは、Fileでの保存にしか対応していなかったアプリケーションにDBを導入する際の例です。

  1. まず、Mikadoゴールを書く(図3)
  2. 図3:DBの実装を切り替えられるようにしたい

  3. 次に、このMikadoゴールを簡素に実現できそうな方法を考える(図4)
  4. 図4:最初のナラティブ・アプローチ

  5. 実際にソフトウェアを変更し、さらに必要な事前条件を洗い出しては変更を戻し、出てきた事前条件を試し、と繰り返していく(図5)
  6. 図5:繰り返すことで出てきた事前条件

  7. ある事前条件を達成できたらチェックを付けてコミットする(図6)。そして、次の事前条件に移ってソフトウェアに変更を加える
  8. 図6:1つの事前条件を解消

筆者は、何度もやったことがある作業以外の多くの変更にはMikadoメソッドを利用することが多いです。

慣れてくると、机上でMikadoメソッドのような問い掛けのプロセスを通すことで、見積り時にタスクを洗い出せるようになります。

問題に対してどのようなテストケースを選択するのか => テスト技法

Mikadoメソッドを使うことで大まかに問題の分割をして事前条件として洗い出しやすくなりますが、各事前条件が何を満たすべきかはMikadoメソッドでは決定できません。

プログラマーにとってのソフトウェア開発の多くでは、それをテストケースという形で表現します。どのようなテストケースを列挙するのかを決定する方法としてテスト技法を利用します。ここでは、それぞれの技法や考え方を簡単に紹介します。

同値分割

まず、入力と出力のそれぞれを仕様に基づき、コンポーネントやシステムの振る舞いが同じと見なせる部分に分割します。これを「同値分割する」と言い、この同じと見なせる部分を「同値クラス」と呼びます。そして同値分割した領域(同値クラス)から代表値を実行するテストケースを設計します。原則として、最低1回各同値分割した領域を実行するように設計します。

これにより、大量の似たような値のテストを減らすことができます。

FizzBuzzの場合には、次のように分割できます。

  • 入力の同値分割
     0以下、101以上の数値
     1,2,4,7 などの数値
     3,6,9 などの3の倍数かつ5の倍数ではないもの
     5,10,20 などの5の倍数かつ3の倍数ではないもの
     15,30,45などの3の倍数かつ5の倍数のもの
  • 出力の同値分割
     数値をそのまま印字する
     Fizzと印字する
     Buzzと印字する
     FizzBuzzと印字する
     エラーと印字する

そして、入力として有効な値を「有効値」や「有効同値クラス」、無効な値を「無効値」や「無効同値クラス」と言います。FizzBuzzの例では、0以下、101以上の数値は無効値になります。

無効値のテスト

ソフトウェアテストでは無効値のテストも行います。「xxが-1と入力され、yyが-100と入力されたらエラーを表示する」といったテストです。

このように、複数の入力変数がある無効値や異常値のテストをするときは、無効値を1つだけ選ぶようにしてください。もし、無効値を複数選ぶテストケースしかないと、どの無効値によってエラーになったのかがわからないからです。

境界値分析

「境界値」とは同値分割した領域の両端、あるいは両端のどちらか側で最小の増加的距離にある入力値または出力値のことです。例えば、ある範囲における最小値または最大値です。ソフトウェア開発の歴史的な経験則から、仕様作成時や仕様からソフトウェアにエンコードする時それぞれで境界に関する間違いは発生しやすいです。

境界値分析を行って導いた境界値に基づいてテストケースを設計します。境界値分析で選択する境界値を「ONポイント」「OFFポイント」と言います。

ONポイント
同値クラスにおける「仕様で指定されている境界となる値」
OFFポイント
同値クラスにおける「境界を挟んでONポイントに最も近い値」

各同値クラスでONポイントとOFFポイントを洗い出し(図7)、それらを組み合わせることでテストケースとします。

図7:境界値の例

デシジョンテーブル

入力と刺激(原因)、および、対応する出力と処理(結果)の組み合わせを示す表を「デシジョンテーブル」と言います。基本的には「何らかのルールを表として表現する方法」です。

入力を「条件」、出力を「動作」と呼びます。次のような表で表現します(図8)。

図8:デシジョンテーブルの例

デシジョンテーブルでは処理順序を考慮せず、順不同ですべての条件が一致したときを表現します。また、システムの前回までの状態などが必要になる場合には、既存の状態自体を条件の1つとして設定することで「ある状態からある入力があった場合」をルール化できます。

おわりに

開発者がソフトウェアテストのスキルアップを図る際によくある課題は「何をテストすべきか」と「どれくらいテストをすべきか」の大きく2つです。

これらは、言い換えれば「どのように問題を分割して進めて行けば良いのか」と「どのようなテストケースを選択するのか」とも言えます。

開発者ができる範囲からテストをしていくときの導入として、前者ではMikadoメソッドという高速な試行錯誤によるタスクグラフの作成を紹介しました。また、後者では基本的なテスト技法や考え方の同値分割、境界値分析、デシジョンテーブルを紹介しました。

スキルアップを考えるときに、ただソフトウェアテストを勉強してもなかなか成果につながりにくいので、まずは「自分は何が苦手なのか」を考えてみましょう。実際に手が止まったときにやっている作業と苦手なこととは別のケースもあります。

テストケースがなかなか思い浮かばないときには、そもそも「何のテストをすべきかがわかっていない」などの場合もあります。

これらをきっかけにできる範囲を広げていけば、さまざまなテストに応用できるようになります。タスク管理やテスト技法には他にもさまざまな方法がありますので、徐々に実践してみてください。

テスト自動化研究会 / 株式会社オンザロード
ソフトウェアテストを中心にアジャイル開発や関数型プログラミングを活かしたシステム開発に従事している。現在の主な活動は、アジャイル系カンファレンスでの講演、JaSST 東海実行委員など。主な著書に『システムテスト自動化標準ガイド(CodeZine BOOKS)』がある。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています