見出し画像

データパターンの考え方

たとえば、実装したプログラムをテストする上で、とても重要なのが

 パターン(およびその組み合わせ)

です。

中でもデータ(入力値や出力値)のパターンに関してはプログラムが「引数」だったり「メモリ」だったり、「DB」や「ファイル」などから処理するために必要な入力値を取得した際、必ずと言っていいほど行われる"入力チェック(=validation)"仕様とほぼ同一の組合せを求めることになります。

一般的に用いられる「同値分割」や「境界値分析」などの単項目向けテスト技法もこういったデータの特性を理解していなければ使えませんし、それだけで単項目のテスト設計がすべて満たされるとは限りません。

この観点は、機能仕様を検証する上でほぼすべての品質を確認できる観点となります。

それらの組合せ一つひとつに対して、

 正しい値、正しい組み合わせパターンであれば、正しい動作をすること
 誤った値、誤った組み合わせパターンであれば、異常検出時の動作をすること

を確認する必要があるからです。

プログラムにおいて異常検出のロジックが必要なのは、その後の「値が正しいことを前提とした処理群」で想定外の結果となって欲しくないためです。私たちが、食べ物を口にして「痛い」「マズい」「辛い」などを感じ取るのは、そのまま身体の中に含んでしまうと内蔵が異常な活動(処理)を起こしてしまい、異常動作(病気)や活動停止(死)など身体(システム)に異常をきたしかねないためのチェック処理のようなものです。

リアルの世界で、人間が五感から得られる外部入力値と言うモノはとても種類が多すぎて、一つひとつの種類やサイズごとにチェック処理を設けるのは至難です。

しかし、プログラムの世界ではその組み合わせは数えられる範囲でしかありません。

たとえば、C言語の変数型を見てみましょう。

C言語の変数型とサイズ

たったこれだけです。

外部からどんなインターフェースをプログラムに渡そうとしても、基本的にはこのような型の値しか取り扱えません(実際にはもう少しありますが)。

これだけであれば、一つひとつの変数型を持つ単項目ごとのチェック観点を網羅することは容易いはずです。結合テストになってくるとデータパターンの正常系/異常系網羅を組み合わせることが中心となって機能仕様の検証を行うことになりますが、単項目あたりのテスト仕様に分解すると確認する本質的自体はいたってシンプルなものです。

一般的には

 単体テスト:処理レベル(またはプロジェクトにおいて"単体"と認めた最小レベル)
 結合テスト:機能レベル
 総合テスト:仕様レベル(大抵の場合は、"システム"運用レベル)

というスコープでテストすることになりますが、異なるのはスコープやテストの種類、評価方法に差があるだけで、少なくともプログラムの妥当性検証を行う場合はどの層のテストでも本質は変わらず

 求められるパターンに対する網羅

です。

 単体テストであれば、処理の分岐/条件パターン網羅
 結合テストであれば、動作する機能が求める動作(イベント)パターン網羅
 総合テストであれば、業務/運用/保守上のシチュエーションパターン網羅

となります。

入力値の確認観点には

単項目観点(値単体の妥当性や信憑性を確認する観点)
複合項目観点(他の値や項目と組み合わせることで発生する因果関係の妥当性や信憑性を確認する観点)

の2種類が存在します。

基本的に単項目観点さえ理解してしまえば、複合項目観点はさほど難しくありません。実現できるかどうかをいったん無視して全数テストを検討するのは単純に掛け算で済むからです(難しいのは、品質の高さを維持しながらどこまでパターン数を圧縮できるかのみ)。

とりあえず圧縮することを考えずに組み合わせのパターンを考えるだけでよければ、それは小学6年生で習う「組み合わせ」と同じですよね。

たとえば3つのモノの並べ方のパターン数がいくつあるか?と問われると

このように選択肢3から始まりその選択肢が1になるまで掛け合わせるだけです。

 3 × 2 × 1 = 6通り

ということになります。考え方はこれと似ています。

6パターンのテストケースを持つ単項目と4パターンのテストケースを持つ単項目を持つ画面の全数テストを実施しようと思ったら

 6 × 4 = 24通り

のテストを実施すればいいということになります。

ここまでお話すると分かると思いますが、「それ言い出したら、1画面に100項目とかあったらとんでもないパターン数になってしまうんじゃないの?」と考えてしまうことでしょう。実際、そうなります。

だからこそ最終的にはこれらのテストケースを圧縮する技法が必要になってくるのですが、それは今回は割愛いたします。まずは「単純に組み合わせるだけであれば小学生でもできる」ということだけ覚えておいてください。

複数の項目の組み合わせ(複合項目)の観点は固有観点となっていることが多く、設計さえ確実に行われていればそこから導き出せるものって案外多いですよね。

たとえば、「郵便番号」や「県」「市」などを入力できる画面があったとして、

 〒:132-0000(東京都の郵便番号)
 県:北海道

 市:那覇市

となっていたら、明らかにおかしいとチェックした方がいいですよね?

あるいは、ユーザー登録する画面で「登録」ボタンを押下した時に一意性の仕様要件が高い項目については、既に存在しているかどうかデータベースを確認した方が良いこともあるでしょう。

しかし、裏を返せばこうした固有の条件が機能仕様上で存在しない場合は、その多くはそもそも同じ画面に複数項目存在していたとしても、組み合わせる必要がありません。単項目観点のみで網羅できるのです。

では、「単項目上の観点は?」と言うと変数(引数)1つ分のチェックまたはテストのパターンは

 値として存在しうる種類のパターン数 × 値として存在しうるサイズのパターン数

で成立します。

データパターンの組合せ

ただし、このパターン数を特定するためにはインプット側の制約とアウトプット側の制約それぞれ見比べ、より制約の強い方に合わせなければならないと言う条件が必要になります。

たとえば、画面から入力し、データベースに登録する機能の場合

 インプット :画面
 アウトプット:DB

となります。

たとえば「住所」を登録する機能があったとしましょう。
それぞれINとOUTの項目に対する制約は

 インプット :画面(テキストボックス、MAX制限:255文字)
 アウトプット:DB(NVARCHAR型、サイズ:100文字)

となっていた場合、当然DBのサイズ制約の方が強いわけですから、いくら画面上で255文字まで入力できても、そのまま255文字の値を素通りさせたら必ず桁あふれでシステムエラーを発生させてしまいます。

ポイントは、仕様上の観点保証だけでなく、システム制約上の観点保証も忘れてはならないという点です。

どこで言われているわけでもありませんが、私の頭の中を整理すると上図のような観点で保証する領域が分かれているイメージとなっています(単語的には、おそらく他所では通用しないかもしれません)。

"原則保証""前提保証"というのは「要件上、設計上の原理原則や前提条件を遵守した機能でなければならない」という意味合いで付けています。先述のインプットとアウトプットの関係性などはまさにその例です。

私がエンジニアの頃には、自分が担当する機能については必ず機能ごとにインプットとアウトプットの相対表を作っていました。複数の設計書を並べて読み解くよりも、1つの表にした方が視覚的にも楽ですし、いざという時のチェックも早々に行えるからです。

そうして、本当にチェックすべき制約が整理出来たら、やっと単項目ごとのチェック観点を洗い出します。『単項目』が持つ属性には

・null⇔not null
・文字種(数値、文字、記号、カナ、かな、漢字、外字等)
・全角⇔半角
・暦日(日付や時間としての妥当性)
・禁則文字(DBやOS、プログラム言語が禁止している文字や予約語、インジェクション対策文字等)
・書式(郵便番号やメールアドレスなど、特定の書式が決まっている値)
・エンコード(文字コード)

などがありますよね。機能仕様が明確になっていれば、取り扱う項目一つひとつにたいして、こうした仕様が決定されているはずです(されていなければ、IN/OUTの定義すらできないはず)。

また、単項目のなかでも『範囲』をもつ属性には

・サイズ(文字数等)
・文字バイト
・桁

などがあります。ひとつの値に複数の条件を設定することはまずありませんが無限にOKと言うことはあり得ませんので、原則いずれかを選ばなくてはなりません。

また、数値を取り扱う項目の場合は、符号の有無も関係してきます。

これらを各項目の仕様にあわせて"境界値分析"による組合せパターンを網羅してしまえば、あとは複合項目固有の機能仕様に合わせた部分だけ個別にテストするだけで、結合テストにおけるデータパターンの『属性保証』観点の確認が完了することでしょう(単体テストで各処理の網羅性確認が行えている場合に限る)。

しかし、それだけでは不足です。

属性保証だけでなく、整合性保証の観点も必要になる可能性があるのです。

単項目に対する『整合性保証』の観点は

「この値は形式(属性)上問題ないけど、
 そのまま先の処理で利用して良い資格はあるの?」

の一言に尽きます。

たとえば、「都道府県コード(JIS X0401)」という項目に対して、"70"という値が格納されていたとしましょう。都道府県コードの属性保証チェックでは潜在的に

となっているのがおそらく一般的でしょうか。本来ならば、チェック順序などにも気を遣うところですが、とりあえずサンプルなので度外視します。

これに対して、属性保証上では「問題なし」という判定が返って来たと仮定します。

しかし、このままで本当にこの先の処理で使ってしまってもいいのでしょうか?
そもそもこの"70"と言う値は、コード上適切な値でしょうか?

仮に適切であっても、これから登録するかもしれないデータベースにとって、重複してもいいのでしょうか?

事実、JIS X0401コードに準拠した都道府県コードであれば"70"と言うコードは存在しません。

 "01"(北海道)~"47"(沖縄県)

までしか存在しないのです。仮にシステム仕様として"99"(不明)や、"  "(未設定)という値を追加したとしても、コード体系上に"70"という値が存在しないのであれば、ここで再オペレーションを促す「警告」や、処理中断の「異常」を検出しておかなければなりません。

すなわち、これ以上システム運用の根幹に関わる処理では「扱ってはいけない」と言う判断を下すわけです。

こういった項目単体でみた場合のデータそのものの整合性を確認するのが、この『整合性保証』の観点です。チェック仕様として考慮に入れなければならないことは、たとえば以下のようなものが挙げられます。

照合     入力した値がマスタ情報となる"正しい情報"と一致するかを確認する
重複     データの重複がないかどうかを検査する
リミット   データが規定の範囲内であることを検査する
シーケンス  データリストに対し、それぞれのデータが順番に並んでいるか検査する
カウント   データリストに対し、取得したリスト件数が妥当であることを検査する
ファイル   ファイル名/ファイルパス など、ファイルに対する正当性を確認する
ファイル存在 指定されたファイルが実在し、利用可能であることを保証する
フォルダ   フォルダ名/フォルダパス など、フォルダに対する正当性保を確認する
フォルダ存在 指定されたフォルダが実在し、利用可能であることを保証する

いただいたサポートは、全額本noteへの執筆…記載活動、およびそのための情報収集活動に使わせていただきます。