見出し画像

『ソフトウェア作法』にみるテストの心(マインド) (T1:Pt2:Ch01-2)

『ソフトウェア作法』は、原著が1976年(日本語訳1981年)と、現在2023年から50年ほど前に執筆され、プログラミングの参考書として今でもその名が挙げられることのある、コンピュータプログラミングの世界では古典と言ってよい著作です。

プログラミングの参考書ではありますが、随所に「よいテストをするためのヒント、テストのよい取り組み方」が散りばめられています。主に設計者テスト、「自分が書くプログラムに対して自分で行なうテスト」を想定したヒント/取り組み方ですが、第三者テストをする立場にもヒントになることが多いです。

『ソフトウェア作法』という書籍の紹介は、姉妹編「『ソフトウェア作法』とプログラミングの心(マインド)」(T1:Pt2:Ch01-1)をご覧ください。



“境界”に目を向けろ

扱うファイルの個数、読み取ったデータの件数など、プログラムが振舞い(ロジック)を切り替える“境界”に着目してテストすることの重要性を強調しています。

厳密な定義を与えることはむずかしいけれども、直観的には「境界」とは隣接の値に対するとはちがった反応を示すことをプログラムに強制するようなデータの値のことであるといえる。たとえば、「入力なし」という入力は、たいていのプログラムにとって境界となる。

([1] p.30, 原文ママ)

虫は出るとしたら「境界」、つまりプログラムの動作の極限点で出るのが普通である。それらはまさに「法則を証明する例外」である。

([1] p.25, 太字は引用者)

いくつかの“境界”例

「入力の「行数」を数える」プログラム(第1章1.3)。「行の末尾に必ず行末文字(改行コード)がつく(改行に出会ったら行数を加算)」として:

①空の入力(文字を読み込むループに入らない) / 
②1行だけの入力(ループが1回だけ回る)

(本書中でも読者への問題としていますが、ファイルが行末文字で終わっていなかったらどんな結果になるか? それは故障と言ってよいか、それとも「行」の定義から「仕様どおり」と見なしてよいか?)

「入力のワード数を数える」(第1章1.4)。“ワード”を「空白文字で区切られた、空白文字以外の任意の文字の並び」とするなら(一部筆者が補完):

➀入力がない(空) / ②ワードがない(入力が空白文字のみ) / 
③ワード1個の場合(ワード前の空白文字の有無、ワード後の空白文字の有無も含む) / 
④ワード2個の場合(空白文字とワードの配置のバリエーションを含む)

「ふたつのファイル内容を比較し、差異を出力する」(第3章3.1)では、ふたつのファイル間の相違に“境界”を見ています。(なお、現在よく使われているdiffよりずっと単純なアルゴリズムです)

①ふたつのファイルA, Bが同内容(両者とも空の場合でも、適切に終了する点に留意) / 
途中まで同内容だが、一方が途中で終わっている / 
③ふたつのファイルの同じ行に差異がある

プログラムが振舞い(ロジック)を切り替える“境界”があるということは、そこに同値クラスを見てとることができるということです(ISTQB Foundation Level シラバス 4.0では「同値パーティション」)。
“境界”を見つけることは、「プログラムの振舞いにはどんな“場合”があるのか」「同じ振舞いをするのはどの場合なのか」を明らかにし、整理するということと同じです。

(境界とは違う視点/角度から見る)

(本節の内容は、『ソフトウェア作法』で述べられていることではなく、筆者の見解となります)
先の「ワードを数える」プログラムでは、こんな記述があります。

われわれはwordcountを「語の中」と「語の外」という二つの状態を基礎とするアルゴリズムを使って書いた。もし状態間の遷移を正しく設定し……(後略)

([1] p.30, 太字は引用者)

状態遷移モデルとして捉えることができるなら、状態遷移テストを考えることもできるわけです。

よかったら考えてみてください! 「入力データの件数の境界」に着目した場合と、考えつくテストは同じでしょうか、違うでしょうか?

Fig.01: 「ワード数を数える」の状態遷移図

(2024-01-16 R002。このプログラムを題材にテストデザインしてみた「テスト技法再訪 -- 境界値分析、状態遷移テスト 」(T3:Pt2:Ch02)に“解答例”が載っています。そちらもご一読ください!)

出力の境界

複数のファイルをまとめて印刷するプログラム(第3章3.4)では、出力(入力に応じて印刷されるページ数)に着目し、「用紙1ページ当たりの行数」を同値クラスと捉え、境界を考えています。

  • ページ当たりの行数ちょうどのファイル:ちょうど1ページ印刷されること(印刷後によけいな空白ページが出力されないこと)。

  • ページ当たりの行数+1行のファイル:2ページ印刷されること。

  • ファイルが2つ:ひとつ目のファイル印刷後、改ページして、ふたつ目のファイルを印刷すること。

これらは当然のことながら設計上の留意事項であるわけですが、テスト視点でも重要なことでもあります。

「境界」を重要視する理由・おさらい

前にもいったように、プログラマたちは苦い経験から、プログラムの境界はもっとも調べがいのある場所だということを学んだ。というのは、そこが虫のもっとも出やすい場所であるからだ。またもしプログラムが極限的な状況で正しく動くことを示すことができれば、そのことは中間の状況で正しく動くことの強い論拠になる。だから、境界に目をつけた極限的なテストを少数用意することは、でたらめに選んだテストでじゅうたん爆撃をするよりははるかにまさる。

([1] p.30)

G.J.マイヤーズの『ソフトウェア・テストの技法』『ソフトウェアの信頼性』でも言及されていることで、それだけ“真実”、昔からエラー(誤り)の多発地帯ということですね(筆者のプログラマー/SE時代にも“真実”でしたし、プログラムを書くのがヒトである限り今も今後も変わらないでしょう)。


早期テスト、“トップダウンテスト”

こうしたテストを、プログラムを一通り書き終えてから実施するのではなく、プログラムの設計段階で構想し、プログラムを書きながら実施するのがいいぞというのは、本書の重要なメッセージです。

プログラムは、最終的にどういうものができるか完全に知らないうちに書き始めることが可能であるのみならず、ときにはそういう時期からテストを始めることができるものである。

([1] p.217)

漸進的プログラム作成テスト法

第4章では、テキストデータを整列(ソート)するプログラムを取り上げて、全体の構造を設計した後、「データの読み書きをするモジュールの機能は、他のモジュールに見せかけのダミーを使ってテストできる」「これは漸進的プログラム作成テスト法の典型例」と言っています。([1] p.173)
(「漸進的プログラム作成テスト法」は巻末の訳語対照表に載っておらず、原文でどんな語句なのか判らないのが残念です)

データの読み書きをするモジュールはデータの整列とは互いにまったく干渉しませんから(そのように設計している):

それぞれ個別の働きをし、明瞭な、はっきり定まった界面を通してだけやりとりをするような小部分から成るプログラムは、がっちりとからみあって一体となったプログラムにくらべてテストしやすいものである。

([1] p.173)

このやり方で、二つのことが確認できます。

①当該モジュールが思惑通りに動くかどうか
②他のモジュールとのインターフェイスに問題はないか(主に呼び出し側からの確認)

この話題は第5章で再度取り上げられ、本章冒頭の引用箇所の後、

たいていの虫が結局のところ組み立ての最終段階で見つかるのだとすれば、その最上位の段階をまずテストするというのは自然なことというほかないように思われる。またその場合テストは、できるだけ早く、実際のプログラムがまだほとんど書かれていないうちからはじめるのが自然である。

([1] p.218, 太字は原文ママ)

この方針に従えば、プログラムの最上位から下位のモジュールに向かって、適宜ダミーを用いて、各モジュールの振舞いとモジュール間の統合を段階的に確認しながら実装を進めていくことができます。
このやり方をトップダウンテスト(top-down testing、本書中では「下向きテスト法」)と呼び、「下向き設計法(top-down design)および下向きコーディング(top-down coding)の自然な拡張」としています。


統合テストの重要性

このアプローチでは、「個々のモジュールのテスト」と「モジュール間統合のテスト」をごく自然に並行して実施することになります。つまり、「早期にモジュール間の統合具合を確認しよう」という考え方でもあります。

当時から、構成要素間の界面(インターフェイス)にバグが多かった

早期にモジュール間の統合、協調動作の具合を見ることで、「(外部)インターフェイスまわりの欠陥(バグ))を早期に検出できます。

大規模なソフトウェア開発プロジェクトで、システムの各部分を組み合わせてみたら思うように動かなかったために表面化した虫が、実は虫の大部分を占めていたという話はよくある。(略)それ以外の虫についても、個々のルーチンについての念入りな検査では見つからず、それらをプログラムのほかの部分と接触させてみてはじめて表面化した、という例は多い。

([1] pp.217-218)

自然なインクリメンタル統合

このアプローチは第6章で、テキストエディタという大規模なプログラムに取り組む中で(行エディタ/ラインエディタと呼ばれる素朴なものですが、それでも結構大規模で複雑です)もう一度現れてきます。
「テストをして品質が確保されたもの(機能、モジュール)を、その先のテストを補助するための信用できる部品/道具として使う」というのは、まさにインクリメンタル統合の考え方です。

指令行から行番号を取り出す部分から話をはじめよう。明らかにこれは独立した部分であって、ほかの部分についてくわしく知る前に理解し、動かしてみることができる。そうしておけば編集機能のチェックをはじめる段取りになったとき、文書行の間を自由に動き回るための道具としてそれを使うことができる。

([1] p.266)

重要なのは、すべてのモジュール(のコード)を書いてからインクリメンタルに統合するの ではなく、最上位のモジュールから 段階的に 実装を進め、書けたモジュールをテストし(書けていないモジュールはダミー(スタブ)で代替)、それを 段階的に 繰り返す、というところですね。


よいテストはよい構造から

もちろん、こういうことは、作ろうとするプログラム/ソフトウェアの“構造”が整っていなければできません。
従って最初にやるべきは全体の構造の設計、“よい構造”を創ることです。

本書で取り上げているプログラムは比較的小規模のものばかりです。現在のシステム開発における規模感とは比較にならないでしょう。
しかし、本書で強調しているアプローチは、規模の大小に関係なく適用できるものです。むしろ、規模が大きくなればその分「よい構造を持っているかどうか」はソフトウェア/システムの実装とテストに大きく影響します(当社比)。

よい構造を持ったソフトウェア/プログラムを書くためのヒントについては、姉妹編「『ソフトウェア作法』とプログラミングの心(マインド)」 (T1:Pt2:Ch01-1)をご覧ください。


むすび

ここまでの話を読んで、現在ソフトウェア開発で「よいプラクティス」とされていることの萌芽を見てとる人もいるのではないでしょうか。筆者には、根っこにある考え方……「コードを書く前にテストを考える」「統合がうまくできている状態を早期に確立し、維持する」という考え方は通じているように思えます。

(´・ω・`).。oO(「電話テスト」も紹介したかったのですが記事が長くなり過ぎるので割愛しました)


文献・書誌情報

  • [1] Brian W.カーニハン, P.J.Plauger (木村泉・訳) 『ソフトウェア作法』 (原著1976, 日本語訳1981) 共立出版

    • 原著 (日本語訳の扉記載の情報に基づく)

    • Software Tools

    • Brian W. Kernighan, P. J. Plauger

    • 1976初版 (Addison-Wesley)


(2023-12-28 R001)
(2024-01-16 R002。「“境界”に目を向けろ」に別記事への誘導案内を加筆)
(2024-01-19 R003。「漸進的プログラム作成テスト法」が見出しで「漸進的プログラムテスト作成法」になっていたのを修正)


この記事が気に入ったらサポートをしてみませんか?