Fizz Buzz問題は、プログラミング初心者向けの簡単な例題として知られています。
また、誰でもすぐにFizz Buzz問題の仕様を理解することができ、また、プログラミング結果の正誤を紛れなく判定できることから、プログラミング初心者の腕試しのみならず、TDD(テスト駆動開発)の演習として活用され、さらに、ペアプログラミングのライブ教材としても広く利用されています。

ところが、TDD等で作成されたテストコードに、【なぜその値をテストしようと思ったのか】について書かれているものを見たことはほとんどありません。

そこで、テストエンジニアの目線から、Fizz Buzz問題をテストする場合の【テストデータ】について考えてみたいと思います。

Fizz Buzz問題とは

Wikipediaの「Fizz Buss」の項によると、「Fizz Buzz(フィズ・バズ、Bizz BuzzやBuzzとも呼ばれる)は英語圏で長距離ドライブ中や飲み会の時に行われる言葉遊び」とのことです。
1からはじめ、順番に1つずつ大きな数を言って次の人にバトンタッチするだけなのですが、3の倍数のときには「Fizz」、5の倍数の時には「Buss」、3の倍数かつ5の倍数の場合「Fizz Buzz」と言わなければならず、うっかり間違えると脱落するというゲームです。

こんな感じに進みます。「1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz Buzz, 11, Fizz, 13, 14, Fizz Buzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz Buzz, 26, Fizz, 28, 29, Fizz Buzz, 31, 32, Fizz, 34, Buzz, Fizz, ……」

これをコンピュータがおこなうプログラムを作ろうというわけです。

コンピュータに教え込むためには仕様を厳密にする必要があります。Fizz Buzz問題の仕様には、いくつかのバリエーションがありますが、おおむね次のような記述です。

■Fizz Buzz問題の仕様  
1から100までの数に対して3で割り切れる数はFizz、5で割り切れる数はBuzz、3でも5でも割り切れる数はFizz Buzzと表示する。それ以外の数は数字のまま表示する。
上記の仕様は若干の曖昧さを持っているのですが、とりあえず先に進みます。

TDD

TDDとは、テスト駆動開発 (Test-Driven Development)のことです。

プログラミングのときに、対象のプログラミングが上手くできたときに合格となる【テストコード】を先に書き、そのテストに合格する必要最低限なプログラムを【開発】した後に、コードを洗練させる【リファクタリング】を行う方法です。(図1)
【テストコード】→【開発】→【リファクタリング】という短いサイクルをテンポよく繰り返す「開発方法」です(「テスト方法」ではありません)。

Fizz Buzz問題について書かれたウェブページのTDDのテストコードを見ると、多くの場合、「1のときに1」、「3のときにFizz」、「5の時にBuzz」、「15の時にFizz Buzz」となることを確認するテストを書いています。

次に、このテストが通る(合格する)プログラムをガシガシ書いて、テストが通るようになったら、書いたプログラムコードを洗練させます。洗練させる過程(リファクタリング)では、最初に書いたテストが失敗せずに通ることを確認しながら進めることで、開発者は大きなミスをしていないという安心感を持つことができます。

この「安心感を持ってテンポよく開発を進めることができる点」がTDDのメリットなのではと考えています。(といいますか、t_wada氏の受け売りです)

図1: TDDと黄金の回転
出典: 『50分でわかるテスト駆動開発』 (和田卓人)

Fizz Buzzの仕様

さて、前項にて、開発者がTDDのテストコードに書きそうなテストデータとして、「1, 3, 5, 15」を挙げました。仕様でいうと、以下のマーカー部分を開発しようと意識してテストデータとして選んだものと思われます。

■Fizz Buzz問題の仕様  
1から100までの数に対して3で割り切れる数はFizz5で割り切れる数はBuzz3でも5でも割り切れる数はFizz Buzzと表示する。それ以外の数は数字のまま表示する。

だいたい塗りつぶされているのですが、テストエンジニアとしては、「“1から100までの数に対して”という仕様のテストはしなくていいのかな?」とか、「“3で割り切れる数”について、3だけをテストしたら十分なのかな?」といったことが気になります。

また、先に「上記の仕様は若干の曖昧さを持っている」と書きましたが、「0(ゼロ)」に対しては、「“1から100までの数”ではないからエラーを表示する」のか「“それ以外の数”なのだから0を表示する」のかどちらでしょうか?(「3でも5でも割り切れるからFizz Buzzと表示する」という解釈は、上記仕様からは出てきませんが、そういったバグ(エラー)はあるかもしれません。 ← これを“エラー推測”といいます)

おそらくは、「1から100までの数」が仕様の大前提で2行目の「それ以外の数」というのは、「1, 2, 4, 7, 8, 11, 13, 14, 16, 17, 19 , ……」のみを指しているのだと思います。しかしながら、文章としては若干の曖昧さが残っています。

そこで、仕様に下線部を追加します。

「数」についても「小数」のケースが気になるといけませんので「整数」に直しました。

■Fizz Buzz問題の仕様(改)  
1から100までの数に対して3で割り切れる数はFizz、5で割り切れる数はBuzz、 3でも5でも割り切れる数はFizz Buzzと表示する。それ以外の数は数字のまま表示する。 また、1から100までの整数以外に対してはエラーを表示する。

テストをつくる前にテスト設計者が仕様の曖昧な箇所について開発者に確認することはよくあることです。組織が離れている場合には、「Q&A票」をやり取りします。

このときに、マニュアルや商品広告に影響があるような本当に重要な点については仕様書の変更を求めますが、上記のような仕様確認に近いものは「仕様書+Q&A票」で済ませます。(たとえ、仕様書が直っていなくても仕様に対する合意は得られたと考えましょう。「信頼」を基盤として進めないと仕事が増えてかないません)

Fizz Buzz問題のテスト

それでは、仕様の確認が済みましたので、テストをつくっていきます。

今回は同値分割法と境界値分析を使います。

まず、ISTQBの用語集(https://glossary.istqb.org/jp/search)で同値分割法と境界値分析の確認をします。ISTQBとはテスト技術者の資格認定を行っている組織です。

同値分割法(equivalence partitioning)  
ブラックボックステスト技法の1つ。同値に分割したパーティション内の1つの代表値を使用してその同値パーティションをテストするテストケースを設計する。 Synonyms: パーティションテスト(partition testing)
境界値分析(boundary value analysis)  
ブラックボックステスト設計技法の1つ。境界値に基づいてテストケースを設計する。

「境界値」の確認も必要でした。

境界値(boundary value)   順序付けられた値を持つ同値パーティションにおける最大値もしくは最小値。

一般に同値分割法と境界値分析は数直線を引いて説明されることが多いものです。

図2は、個数によって単価が違う(1個~4個:単価200円、5個~9個:単価170円、10個以上:単価160円)場合の同値分割と境界値分析の結果を表しています。

図2: りんごの単価の同値分割法と境界値分析
出典: 『ソフトウェアテスト技法ドリル【第2版】』p. 21 (秋山浩一)

ところが今回の仕様に対して、数直線で表現するのは難しそうです。
試しに出力のパターンごとに色を変えて描いてみました(図3)がどうもうまく整理できません。

図3: Fizz Buzz問題の同値分割法と境界値分析

このまま“色が連続する●”を同値パーティションと考えて境界値分析を行った場合、1から100までの値がすべて境界値となってしまうからです。

このようなときには、数直線を書く前にベン図(同値図、もしくは完全同値図と呼びます)を用いて集合で整理するのがよいです。

今回の場合は図4のように①から④の4個の領域が確認できました。

  • このとき、領域として重なった部分(この例では③)を独立した①②④と同等な領域として切り出すことがポイントです。同値分割ですので分割します。
図4: Fizz Buzzの数値の同値図

このような図にすることによって、同値パーティションの①~④を識別できます。(無効同値を考えるなら①~④の4パーティションに加えて、外側の領域1つを含めた5個の同値パーティションとなります)

それぞれの同値パーティションがISTQB境界値の定義に書かれている「順序付けられた値を持つ同値パーティション」となっていることに注意してください。 あとは、各同値パーティションにおける最大値と最小値を境界値としてテストデータに採用するだけです。

テストデータは表1の通りとなります。

表1: Fizz Buzz問題の同値パーティションとテストデータ(境界値)

同値 パーティション 意味 テストデータ (境界値) 期待値
領域① 3でも5でも割り切れない 1, 98 同じ数字
領域② 3で割り切れる 3, 99 Fizz
領域③ 3でも5でも割り切れる 15, 90 Fizz Buzz
領域④ 5で割り切れる 5, 100 Buzz
無効同値 パーティション 1から100の範囲外 0, 101 エラー
テストデータについて、TDDの「1, 3, 5, 15」の4個と比較すると「0, 1, 3, 5, 15, 90, 98, 99, 100, 101」の10個に増えてしまいましたが、テストの意図は明確になったのではないでしょうか。

Fizz Buzz問題の仕様(未来スライダー)

先に、Fizz Buzz問題の仕様について考えましたが、これは、あくまでもプログラム初心者の演習課題としてつくられた仕様です。
もしも、人間のFizz Buzzゲームに参加するようなプログラムをテストするとしたら、どうでしょうか?
その時には、音声出力につなぐでしょうから、イレブン(11)とツェルブ(12)についてテストしたくなるかもしれません。また、他の人の回答(音声)を認識することや、ひょっとしたら、日本では「イチ・二・サン…」、英語圏では「ワン・ツー・スリー…」、フランス語圏では「アン・ドゥ・トゥロワ」で答えるための言語設定機能のテストをしたくなるかもしれません。

図5: Fizz Buzzの数値の同値図
出典: 『ワークに向けてのHAYST法全体図のおさらいとワークの全体流れ』 (JaSST東北実行委員会)

出典: 『ワークに向けてのHAYST法全体図のおさらいとワークの全体流れ』 (JaSST東北実行委員会)

図5は、2018/5/25に開催されたJaSST東北にて発表された「未来スライダー」です。テスト対象に期待される品質について、リリース時点という「ポイント」で考えるのではなく、未来を捉えた継続した時間軸で考えることの大切さを表しています。

このように、「テストは条件次第(テストの7原則の6)」ですので、テストエンジニアは「状況が異なれば、テストも変わる」ことを常に意識する必要があります。

まとめ

Fizz Buzz問題を題材として、TDDとテストについて考えてきました。ここで、TDDでのテストデータが「1, 3, 5, 15」の4個と少ないことで、TDDについての不信感を持った方がいたら、それは違います。

TDDでは開発者がコードの内容を熟知したうえで厳選したテストデータを用いてテストをしています。したがって、何らかの技術的な確信をもってテストデータを選択すればよいのです。テスト技法が見つけた数との違いにこだわるよりも、開発者が「このテストで十分、自分のプログラムの動作を確認できる」と納得することが大切です。

一方で、テストエンジニアは、プログラムコードを読まない人に対しても、何を意図したテストを行い、テストでどれほど網羅しているのかの基準や結果を示すことで、テスト対象に対する安心感を与える必要があります。

Fizz Buzz問題は単純なプログラムですから、現実のテスト対象とのギャップは大きいとは思います。しかしながら、テストに対する基本的なアプローチとしてご参考になれば幸いです。

SHARE

  • facebook
  • twitter

SQRIPTER

秋山 浩一(あきやま こういち)

㈱日本ウィルテックソリューション・ITコンサルタント

記事一覧

NPO法人ASTERの理事としてテストセミナーの講師などをしている。また、JSTQBステアリング委員。著書に『ソフトウェアテスト技法ドリル 第2版』、『ソフトウェアテスト講義ノオト』、『事例とツールで学ぶHAYST法』などがある。博士(工学)

RANKINGアクセスランキング
#TAGS人気のタグ
  • 新規登録/ログイン
  • 株式会社AGEST
NEWS最新のニュース

Sqriptsはシステム開発における品質(Quality)を中心に、エンジニアが”理解しやすい”Scriptに変換して情報発信するメディアです

  • 新規登録/ログイン
  • 株式会社AGEST