ソフトウェアの品質を学びまくる

ソフトウェアの品質、ソフトウェアテストなどについて学んだことを記録するブログです。

仕様スニペット002「アカウントロック」の #テスト設計パターン を考えてみる

5月3日に、2つ目の仕様スニペット・「002: アカウントロック」を公開しました。

docs.google.com

「テスト設計パターン」「仕様スニペット」って何?という方は、こちらの記事を先にご覧くださいね。 www.kzsuzuki.com

仕様スニペット002: 「アカウントロック」

以下の仕様に示されるアカウントロックについて、テスト観点を考えてください。

  1. ユーザはログインの際に、アカウント名とパスワードが求められます。
    ※アカウント名・パスワードの文字列長・文字種などについては、議論の対象外とする。
  2. アカウント名とパスワードの組み合わせが正しい場合、ログインが成功します。
  3. アカウント名とパスワードの組み合わせが正しくない場合、ログインが失敗します。
  4. あるユーザ名に対し、ログインが3回連続で失敗した場合、アカウントがロックされます。
    ※アカウントロックの解除については、議論の対象外とする。
  5. ログインが成功した際に、ログイン失敗回数はゼロにリセットされます。

001が、わたしの意図する仕様スニペットの位置づけに対して少し複雑だったように感じて、今回はかなりシンプルなものです。
今回も、何人かの方が検討をしてくれました。それぞれを読んで思ったのですが・・・、

優秀なQAの人たちの習慣として、仕様を提示されたら、

  1. 内部の作りがどうなっているかを想定し、それを開発者に確認しようとする
  2. 仕様自体も間違っている、網羅性が低い、考え漏れがある前提で、仕様のより良い姿を見つけようとする

んですよね。
もちろんこれは、いいことだとわたしは思います。ですがそのことが、「テスト設計パターン」の成立を難しくさせるなとも感じました。なぜなら、「シンプルで完全な仕様」というものは、提示し得ないからです。

それはさておき、先に進みましょう。

回答の例

かいりさんの記事

前回に続いて記事化してくださいました。

note.com

どの部分も頷けるものばかりなのですが、特にいいなあと思ったのが、「ロックに至る理由が、3回連続失敗とは限らない」と判断している点です。ユーザのミス以外にもロックさせる契機はありうると考えて、「連続失敗回数」と「ロックされている/いない」の情報は分離されていると想定しているんですね。仕様を鵜呑みにしているだけだと、そこまで考えられないかもしれません。
で、失敗回数・ロック有無の2つの情報と、ユーザ名とパスワードの組み合わせが正しいかという3点をもって、デシジョンテーブルを作成しています。

デシジョンテーブルを書かれる過程を読んでふと思っていたのですが、わたしとは思考の順番が逆かもな?と思いました。
わたしの場合、条件整理→テーブル じゃなくて、テーブルを書きながら条件整理をしているようなところがあります。

おいでさんのコメント

パスワードを忘れた場合のパスワードリセットの仕様を考慮するかしないかが悩みどころかもしれないですね。

パスワードリセット機能や、別の理由(サブスク料金未払いなど)でのアカウントロックについては今回は対象外としますが、これらもこれらで面白そうですよね。

アカウント名hide_ramen_sanのパスワードがpasswordだったとして、hide_ramen_sanでパスワード3回間違えたらロックかかるけど、パスワードpasswordに対してアカウント名を3回間違えても(hide-ramen-sanとかhideramensanとか入れても)ロックかからないという仕様になると思うんですよね。

「パスワード側を固定した不正ログインアタック」みたいなのはかいりさんも言及されていましたが、これも対象外で・・・。

𝓜𝓪𝓼𝓪𝔂𝓾𝓴𝓲 𝓣𝓪𝓬𝓱𝓲𝓫𝓪𝓷𝓪さんのコメント

一般ユーザーか管理者権限ユーザーかによって、ロック条件が変わりそうですが、今回のは一般ユーザーのものとして、管理者権限ユーザーは、別問題でよいでしょうか。

わたしは一般ユーザしか考えていませんでしたが、確かにユーザ種別によって仕様が違うケースもありそうです。

miwaさんの記事

miwa719.hatenablog.com

わたしの設問に対するものではないのですが、「ログイン機能」を与えられたときにmiwaさんが考えるテスト観点についての記事です。

たった3行の文章からここまで広げられるのにやっぱり脱帽ですし、仕様を所与のものとせず、「なにがうれしいの?」「なんのために」「ほんとうに」「なぜ」と問うているのもさすがですよね。

おわりに

与えられた仕様から、みなさんが何を考え、どう振る舞い、どんなテストケースを設計していくかを知るのは、自分自身とてもいい勉強になります。「テスト設計パターン」などと仰々しいものでなくても、「こんな仕様にはどんなテストする?」とワイワイ話すだけでもいい気がしてきました。

が、もうしばらくやっていきます。
第3、第4の仕様スニペットは、かいりさんが考えてくれましたので、近日Twitterにて公開します!

自然言語からデシジョンテーブルを生成する、GIHOZ AIを触ってみた

ベリサーブ社が公開しているソフトウェアテスト技法サービス「GIHOZ」で、ベータ機能として「GIHOZ AI」が公開されました。 www.veriserve.co.jp

4月時点で利用できるのは、「仕様書などの文字情報からデシジョンテーブルを自動生成する」というものです。これまでは人間がモデルを描いていましたが、その部分をAIに任せられるということになるでしょう。

タスク 従来のGIHOZ GIHOZ AI
仕様の作成 人間 人間
モデルの作成 人間 GIHOZ
テストケースの作成 GIHOZ GIHOZ

これはMBT(Model Based Testing)の夢が広がりますね~。

さっそく触ってみる

さっそく試してみましょう!

GIHOZ AIのUI

こんな感じのUIで、レスポンスは概ね1分以内。30~40秒程度でしょうか。デシジョンテーブルをの生成は、1日あたり5回まで試すことができます。

それでは、以下の順に試していきたいと思います。

  1. シンプルな仕様
  2. 少しひねった仕様
  3. 整理されていない仕様
  4. 論理関係がややこしい仕様
  5. ハルシネーションを起こしやすそうな仕様

先に結果を書いておくと、以下のようになります。

  • 1、2、3は問題なく生成できる
  • 4のように、仕様の記述がひどいと、結果も間違いやすくなる
  • 5のように、特殊な仕様を入れると、動作が怪しいことがある

自分の作ったGPTsが一瞬でプロンプトをさらけだしてしまったことがあるので、プロンプトインジェクションにも興味があります。が、これはセキュリティ攻撃みたいなものなので、当然他社のサービスには試せません。

シンプルな仕様

『ソフトウェアテスト技法ドリル』のデシジョンテーブルの章の問題で試してみましょう。

あるECサイトでは、「商品カテゴリーが書籍である」 かつ 「価格が1,500円以上である」 かつ 「配送先が離島でない」 場合に「送料が無料」となる。

シンプルな仕様

さくっと正解です。いやこれ、当たり前のように出力されますけど、素晴らしいことだと思いますよ。従来は、このテーブルを人間がピコピコ作っているのですから。
「未満1500円」という文言は気になりますが、もう一度生成させてみると、「1500円未満」となりました。

少しひねった仕様

上の例より若干複雑にしたものです。

仕様1: 書籍 かつ 1,500円以上 かつ 離島以外 なら、配送料無料
仕様2: 書籍 かつ 1,500円以上 かつ 離島 なら、配送料300円
仕様3: 書籍 かつ 1,500円以上でない なら、配送料400円
仕様4: 書籍でない かつ 離島以外 なら、配送料500円
仕様5: 書籍でない かつ 離島 なら、配送料1,000円

少しひねった仕様

こちらも正しく生成できています!

整理されていない仕様

上の2つの例は、仕様の記述がかなり整理されており、言ってみれば文章そのものが「モデル」相当になっていると言えます。まあ仕様書であれば当然かもしれませんが、とにかく生成AIには扱いやすいものと言えるでしょう。

ということで、もうちょっと整理されていない感じで書き直してみましょう。

書籍で1500円以上なら配送料は無料だ。ただし離島の場合は300円かかる。
書籍でない場合は配送料は500円。離島なら1000円にする。
書籍であっても、1500円未満なら400円の配送料を課す。

日本語としてそこまでひどくはないのですが、「ただし」で後出しジャンケンしたり、出てくる金額が「購入金額」なのか「配送料」なのかが曖昧だったり、仕様の記述としてはイマイチですね。

整理されていない仕様

素晴らしい、正解です!
この程度の「いまいちな記述」なら、しっかり理解してくれます。わたしなら、こういう仕様記述は間違いなくナオシを入れます。

論理関係がややこしい仕様

論理関係の表現を無駄にややこしくしてみましょう。

(書籍でない かつ 離島でない)ではない場合は配送料100円。
書籍でない かつ 離島以外の場合は配送料200円。

これはもう嫌がらせですね。
1行目の仕様はド・モルガンの法則から、「書籍である または 離島である、ならば配送料100円」。2行目の仕様はその補集合です。

論理関係がややこしい仕様

ここまでヒドイ記述だと、さすがに誤りが発生してしまいました。
おそらく1行目の「ではない」を「離島でない」にのみ作用させているのではないかと思います。そのため、この2行でカバーできないケースを「無料」と補完したのでしょう。入力文章の方が悪いですね。。

このように、「そもそも仕様の記述が整理されていない」場合は、先に別の生成AIに食わせて整理した方がいいかもしれません。ChatGPTでは以下のように答えてくれました。

指定された仕様をもとに、論理関係を整理して分かりやすく書き直します。

  1. 「(書籍でない かつ 離島でない)ではない場合は配送料100円」という条件は、論理演算において「または」を用いて書き換えることができます。つまり、「書籍である または 離島である」場合、配送料は100円です。
  2. 「書籍でない かつ 離島以外の場合は配送料200円」という条件は、書籍ではなく、かつ、離島でもない場合に配送料が200円となります。

整理すると以下のようになります:

  • 書籍である場合、または、配送先が離島である場合、配送料は100円です。
  • 書籍ではなく、配送先が離島でもない場合、配送料は200円です。

ChatGPTに直してもらった後

正しく生成されました。

ハルシネーションを起こしやすそうな仕様

どういう仕様ならハルシネーションを起こすかを考えてみましょう。

よく引き合いに出される例ですが、「日本で一番高い山は」と文字列が続くと、その次には「富士山」という単語が来る確率がもっとも高いので、LLMはそれを返している(というのが今のわたしの理解です、違ったらスミマセン)。
逆に、「生成AIが”次の単語”として想定する流れを裏切るもの」があれば、間違いを引き起こすのでは?と考えました。

FizzBuzzっぽいけど違う

プログラミングの例として Hello World! の次に多そうな、FizzBuzz問題をちょっと変更して聞いてみましょう。

入力された整数に対し、その整数が3の倍数なら「Fizz」、6の倍数*1なら「Buzz」、両方の倍数なら「Fizz Buzz」、いずれでもなければ元の整数を返す。

FizzBuzzっぽいけど違う

全然だまされませんね。

うるう年っぽいけど違う

偽うるう年判定でも試してみましょう。

グレゴリオ暦法では、うるう年を次のように決めています。
(1)西暦年号が4で割り切れる年をうるう年とする。
(2)(1)の例外として、西暦年号が100で割り切れて200で割り切れない*2年は平年とする。

うるう年っぽいけど違う

だいぶおかしなものが出てしまいました。
これをハルシネーションと呼ぶべきかはわかりませんが、本来は全8パターン(23)個あるべきものが3パターンになっています。ありえないケースを自動的に圧縮した可能性も考えましたが、テストケース#2は「100で割り切れる かつ 4で割り切れない」ものとなっており、そういうわけではなさそうです。条件がすべてNになるケースも漏れています。

もう一度生成してみましょう。

うるう年っぽけど違う2

悪化してしまいました・・・。

うるう年

現実のうるう年の定義と異なるせいかもしれません。正しい仕様に戻してみましょう。

グレゴリオ暦法では、うるう年を次のように決めています。
(1)西暦年号が4で割り切れる年をうるう年とする。
(2)(1)の例外として、西暦年号が100で割り切れて400で割り切れない年は平年とする。

うるう年

今度は適切ですね(F・H・I・Jのチェックは手動で外しています)。

「うるう年っぽいけど違う」で正しくテーブルを生成できなかった理由が、「生成AIが”次の単語”として想定する流れを裏切るもの」だったせいかどうかはわかりません。ただいずれにせよ、生成AIの回答は正確さが保証されたものではないことに、あらためて注意です。

これは当然ながら、GIHOZ AIの「利用上の注意」にも記載されています。

GIHOZ AIは現在ベータ版であり、本機能で出力される内容には不正確な情報が含まれる可能性があります。本機能を利用するときは必ず結果を確認し、ユーザ自身の責任において利用の可否を判断してください。また、ユーザが入力した内容によっては結果を出力できずエラーになる場合があります。

まあ後半の方の例は、「生成AIの限界」云々以前に「仕様が終わってるよね」って話だと思います。。

まとめ

触ってみて、「まともな自然言語で書かれた仕様であれば、おおむね妥当なデシジョンテーブルを作ってくれそうだ」という感触を得ました。
素晴らしいですね。テスト設計技法のツールという分野を開拓していくベリサーブさんのチャレンジに感謝です。

一方で当然ながら、Garbage In, Garbage Outの原則にも注意はいるだろうと感じます。
生成AIは自然言語で対話できるうえに、ある程度妥当なコンテキストも補ってくれますが、やはり「文章を論理的かつ簡潔に記述する力」が、人間側にまだまだ必要だと感じました。これによって、生成AIを利用したプロダクトの力を引き出せるのだろうと思います。

Generated By Dalle

*1:本来は5の倍数

*2:本来は400で割り切れない

#テスト設計パターン を考える会 をTwitterベースでやってみた

少し前から、「テスト設計パターン」(Test Design Pattern)というものを考えています。
その定義を説明する前に、なぜこんなことを考え始めたかについての背景から。

テスト設計パターンとは何か

背景

ソフトウェアテストの設計技法は、書籍をはじめとして各所に情報がまとまっており、学習の難易度はそれほど高くないと思います。一方、一通り学んで、さあ使おう!という段階になると、学んだはずの技法がすんなり適用できないことがあります。
その理由は、大きく以下の2つが考えられます。

  1. 書籍に出てくるような練習問題と違って、現実の仕様とテスト設計技法とは、1:1でキレイに対応しているとは限らない。仕様を解きほぐしていくことで、ある側面は技法A、別の側面は技法Bと対応づいたりする。
  2. 「よくある仕様に対する、よくあるテストのパターン」、いわゆる「定石」のようなものがあるが、特別なテスト設計技法として扱われていない。各々の経験知で都度、テストケースを作っている。

「テスト設計パターン」という名前で扱いたいのは、この2.です。

「テスト設計パターン」の定義0.1

以下のように、仮に定義しておきます。

テスト設計パターンとは、ソフトウェア開発において現れることの多い仕様に対し、行うべきテスト設計の形をパターン化したものである。

ここでいくつか言い訳をしておきます。

  • 「テスト設計パターン」という、いかにも以前からありそうな言葉は、もちろんわたしの発明品ではありません。上述の定義は、完全にわたしのオレオレ定義にすぎません。
    • にしさんが過去に言及しています。
    • 辰巳さんによると、『Testing Object-Oriented Systems: Models, Patterns, and Tools』(Robert Binder著)に「Test Design Patterns」があるとのことです。わたしはこの内容をちゃんと確認していません。。
    • ベリサーブさんでも、これに相当する(だろう)ものを「MBTパターン」と呼んでいるそうです。
  • わたしは、「デザインパターン」「パターン言語」についての知識に乏しいです。

仕様スニペットとは何か

「仕様スニペット」の定義0.1

さて、「背景」には「よくある仕様」と書きました。テスト設計パターンとやらを考えるには、それに対応する仕様が必要です。ですが現実の仕様は複雑で、そのテストを考えるには多くの側面に注目する必要があるため議論が発散し、パターン化が難しくなってしまいます。
そこで、現実の仕様を特定の要素だけに絞り込み、テスト設計の議論をしやすくしたものが必要です。これを「仕様スニペット」と呼び、以下のように仮に定義しています。

シンプルかつ抽象化された仕様の断片

  • 「シンプル」というのは、議論したい要素にのみ情報を絞り込んでいるという意味です。
  • 「抽象化された」というのは、特定のドメイン・開発技術・ツールへの依存性が低いという意味です。 その分コンテキストに乏しくなるのが欠点です。
  • 「ソフトウェア仕様の再利用可能なベストプラクティス」ではありません

たとえば『ソフトウェアテスト技法 練習帳』に出てくる練習問題は、特定のテスト設計技法を適用することに注目できるという意味で、この定義に当てはまると言えます。

「仕様スニペット」という名称はお察しの通り、「コードスニペット」という言葉に倣っています。ただ、specification snippetという単語もわたしの発明品ではなく、たとえばノーコード開発ツールのUnqorkでは、ドキュメント作成の文脈でこの言葉を使っています

仕様スニペットの目的

  1. 「仕様に対するテスト設計のパターンを議論すること」を主目的としています。
    • ソフトウェア開発において「何かよく見るよね」という仕様をスニペットとして切り出すことで、その側面のみフォーカスしてテストの議論ができるようになることを期待しています。
    • 一つのスニペットに情報を盛り込みすぎると、テスト設計の議論が発散するため、上述でいうシンプルさが重要です。
    • ドメインやツールへの依存が強いと、汎用的な議論がしづらいので、上述でいう抽象度が必要です。
  2. 仕様自体の良し悪しも議論できるかもしれませんが、わたしの期待はそこではありません。

仕様スニペットの例

1つ目の例として、以下のようなものを考えてみました*1
これはお試しに作ったもので、上で提示した定義にふわしいものか、フォーマットとして適切かはまったく煮詰まっていません。

仕様スニペット001: 「1:n構造のエンティティ削除」

以下の仕様に従う2つのエンティティの追加・削除についてのテスト観点を考えてください。

# 仕様
1 2種類のエンティティAとBには親子関係があり、Aが親、Bが子となります。 Aが部署、Bが社員、といった関係です。
2 親Aの数は、全体で0個以上64個以下です。この範囲で追加・削除ができます。 部署は最小0個、最大64まで。
3 子Bの数は、全体で0個以上128個以下です。この範囲で追加・削除ができます。 社員は最小0、最大128まで。
4 親Aは、0個以上16個以下の子Bを持つことができます。 部署には複数人の社員が所属することができます。1人の場合もあり、また0人の場合もあります。
5 子Bは、必ず1個の親Aを持ちます。 社員はただ1つの部署に所属することができます。兼務はありません。
6 子Bは、いつでも削除することができます。 社員は常に削除可能です。
7 関連づく子Bが1つ以上存在する限り、親Aを削除することはできません。 社員が残っている部署を削除することはできません。

この時点でスニペットのタイトル(削除)と問題(追加と削除)がブレていることがわかっていただけると思います。。
最初は、「削除」だけを考えておりまして、数の最小最大の仕様などもなかったのですが、「いくらシンプルにといっても、これだけでは・・」と付け足しているうちに、仕様が膨らんでしまっています。

一方で、この問題に取り組んでくださった方は、「変更」(たとえば社員単体の情報の変更や、社員の部署変更など)を考慮されており、このスニペットがどうも中途半端であることがわかります。
おそらく、仕様スニペットとテスト設計パターンは、交互に行き来しながらバランスを探っていくべきなのでしょう。

回答の例

Twitterで、たくさんの方が反応してくれました。見逃しあったらゴメンナサイ。
みなさんの記事やコメントに対して、感想を書いていきます。

かいりさんの記事

note.com

  • 仕様から内部構造を想定したうえで「要確認」としているのが好きです。特にここでいう「削除」が論理削除か物理削除なのかは、かいりさんご自身の記事にもあるように複数の実現方法があるので、重要ですよね。 note.com

  • 更新については仕様で何も言及しなかったのですが、部署変更を意図したテストをされているのが面白かった。上述の通り、仕様スニペット側に反映すべきもの。

  • 部門と社員の例だと、多対多を考えるべきで、中間テーブルはあるのだと思います。今回は、1:n固定で考えていたので、例が悪かったかも。
  • それぞれのテストケースはもちろん、「観点の決め方」が、まさにほしかったものでした。仕様を見てどういう思考過程が働くか、が大事なので。引用してみます。
    • 数字が出てきた時点で境界値分析が必要だと考えました。
    • 状態遷移も考えましたが、単純に人数だけなので要らないと考えました。
    • 親あたりの子の制約があるので従業員の追加と更新に関してのみデシジョンテーブルを使うと思います。(複数の条件がある場合はデシジョンテーブルを使う)
    • 追加と削除と聞くと、CRUDの他の(RとU)も要らないか考えました。
    • 中間テーブルの可能性を結構考えましたが、問題文を読んで意図的に削除しました。

おいでさんのコメント

その親と子がどういう使われ方をするかイメージ出来てないのですが、親が営業部、子が関東支部と関西支部みたいな感じでXさんが営業部関東支部、Yさんが営業部関西支部みたいな感じであれば、親に子が紐付かないのが有り得るとして、Zさんが営業部の子の所属無しが有り得るのかは気になりました。

これはおそらく仕様の伝え方が悪くて、わたしは「2つのエンティティが親子関係になる」ことを表現したかったのに対し、おいでさんは「1つのエンティティが入れ子で親子関係を構成する」のを想像したものと思います。ER図で、自分自身に矢印が伸びるやつ。

子が紐付かない親と、子が紐付いてるけど親だけを使うことが出来るか?ということです。
あと、子の削除と紐づけの解除が違う仕様の場合もあるので、そうじゃないかは確認したいです。

この後者の部分は、仕様からまるって抜けていますね。関連付けを削除する=子を削除する だと、「関連付けの変更はできない」ことになってしまうので、確かに仕様として考えづらいです。

やまずんさんの記事

uskymstp.hatenablog.com

数の最小・最大に注目するところはかいりさんと同じですが、デシジョンテーブルを使っているのが特徴です。
Twitterでも直接会話しましたが、今回の仕様における最小・最大条件は組み合わせを考慮しなくていいので、わたしならデシジョンテーブルは使わないと思います。

こちらのコメント

親を追加できない状態、親を削除できない状態、子を追加できない状態を状態遷移図で表現したかったけど、私では無理でした。

もそうなのですが、「どのような時に、デシジョンテーブルや、状態遷移図を使わないのか」って意外に説明が難しいな?と感じました。これもテスト設計パターンの1つのテーマになるなと考えています。

植月さんのコメント

ロジックの確認にデシジョンテーブル、境界の確認に境界値分析、あと追加と削除の操作シナリオの確認。これは状態遷移図を書いて確認するかも。

植月さんも、デシジョンテーブルと状態遷移図を挙げているので、もうちょい詳しく知りたいところ。
シナリオの観点は、他の方にはあまりなかったかも。わたしも、追加・変更・削除の単機能をつなげたシナリオやりたくなります。

しんすくさんのコメント

親子構造を持つリスト、と考えて、リストのテストのアプローチとして、要素の数の境界値と重複、順序、型、追加削除の機構、親子構造のアプローチとして親の成立、子の成立、仕様上の禁則。とかどうだろ。

重複・順序あたりは他の方にはなかったように思うので、これももうちょい知りたい。

scranbleさんのコメント

関連付けなしで単に親、子の個数の境界値分析 親子の関連付けで子の個数の境界値分析 関連付けが重複してないかどうか (コピーありだと子が溢れるけどスコープ外)

こちらでも「重複」観点が出ていますね。わたしにはピンとこないので、詳しく知りたい。

アシカさんの記事

tyngw.hatenablog.com

  • クラス図で絵にして、さらに例には現れていない「会社」も追加しているのがよかったです。また「部署が0のとき、社員が追加できないことを確認する」も目からウロコでした。出題者なのに。

    分析クラス図を作ると、どの境界をテストしたいのかが明確になるような気がしますよね?

  • かいりさんと同様、アシカさんの記事でも、追加・削除に加えて変更・参照を考慮しています。

  • また、同時実行の観点(誰かが削除中のものを、別の誰かが参照、など)も考慮しています。データの整合性やユーザの体験に直接関係するので、大事な観点でうしょね。

テスト設計パターン自体についてのコメント

あきやまさんのコメント

「テスト設計パターン」と「テストアーキテクチャ設計」はお隣さんの距離の技術なのではないだろうか? もしそうならにしさんが考えた「テスト観点を用いて【テストタイプ】を文法構造で整理する方法」が参考になるかもしれないと思いました。 これより細かなテスト設計だとテスト技法になりそう。

これはまだピンと来ていません。テストアーキテクチャ設計と近いですかね・・・?

くっきぃさん&みずのりさんのコメント

みなさんもお気づきの通り、「仕様が曖昧/悪いのでは?」という感触もありまして、やはり仕様とテスト設計に行ったり来たりして、適切なバランス・抽象度に収束させていきたいなと感じます。
以下の、くっきぃさん&みずのりさんのコメントが、その通りだと思う。

昔、社内でコンテキスト特化した感じでテスト設計のパターンかあるだろ?と盛り上がりかけたところで、テスト設計パターンよりも仕様パターンを考えた方が良くない?という話になって消滅した、どちらとも、というのを思い出した。

 

仕様パターンに典型的なテストがついてくる、ってやつが無難かなー、スペシフィケーションあんどテストデザインパターン セットで情報まとまってると便利そう、だけど種類多すぎて毎回考えたやつがどんだけ流用できるんかねーと思うとこも

で、何がやりたいのか?

ともあれ、このような「仕様スニペットとテスト設計パターン」のペアをいくつかみんなで議論すると、なんかいい感じにフォーマットや粒度が定まってきたり、パターンが整理したりできるようになるのでは?というのが期待です。
スニペット002も考えてみるので、興味ある方、Twitterで反応いただけると嬉しいです。

その場合の留意点は、Twitterにも書きました。

  • お題に対し、テスト観点を「もれなく洗い出そう」とやり始めたらキリがない→やる気なくなる になってしまいます。「漏れを指摘されると恥ずかしいから…」とかなっちゃいますよね? なので、「たとえばこんなテスト観点」でいいのです! その「たとえば」をコレクションしたい。
  • テスト設計における常套句ですが、「パーフェクトな正解」はないと思ってます。特にこの企画は、仕様がザックリだしコンテキストもないので、なおさらです。逆にだからこそ、本質的で典型的なテスト観点の話ができるかも?とも思ってます。 まあ、大喜利だと思って取り組んでみてもらえれば〜。

情報が集まってきたら、何らか小さいコミュニティを作って議論してみるのもいいと思いますが、まずはTwitterベースでみなさんと議論できればと思っています!
「てか、おまえの答えは?」となるかもしれませんが、記事が長くなりすぎたので、、いったん記事を締めたいと思います。

Generated By Dalle

*1:Twitterで提示したものから、少し表現を変更しています。

「一意な値」パターンのテスト設計を考える

おいでさんのツイを見て、「あっ、これ!」と思いました。
テスト設計技法って、たとえばデシジョンテーブルテストとか状態遷移テストなどいろいろありますが、「テスト対象として割とよく遭遇する割には、”テスト設計技法”みたいな形ではまとめられていない」ケースがけっこうある気がするんですよね。

おいでさんの例は、シンプルでわかりやすいので、自分がどう考えているのかを整理してみました。

サマリ

  • ありがちな仕様なのに、テスト設計の定石がそこまで広まってないものがある気がする。
  • 「一意な値」パターンは、CRUDとライフサイクルの2つを考えるとよさそう。
  • テスト設計は、仕様の曖昧さを減らすことのできる活動である。早く始めよう。
  • 思いついたケースを無造作に追加していくのではなく、そのケースからテスト観点を逆導出すると、網羅性が考えやすくなる。

対象とする仕様

仕様は以下とします。ここでは「一意な値」パターンと呼んでおきましょう。

  1. ユーザは、固有のユーザIDを持っている。ユーザIDを変更することはできない。
  2. ユーザは、ユーザ登録時にユーザ名を決めることができる。
  3. ユーザは、ユーザ名を変更することができる。
  4. ユーザ名は一意であり、重複することができない。すでに使用されているユーザ名を使おうとするとエラーとなる。

これ以外の要素、たとえば以下のようなものは無視します。

  • 文字列長、文字種といった、入力値の属性についての仕様
  • 変更したユーザ名がシステム全体に反映されるまでの時間差みたいなもの

テスト設計1: まずCRUDを考える

おいでさんが例として挙げられている3つのテストケースは有効だと思います。

  1. すでにAがある時に別の人がAを登録しようとする
  2. BさんがAに変更しようとする
  3. Aを削除してから別の人がAを登録しようとする

ただわたしの性格的に、リスト型のテストセットは網羅性が見えづらく、テーブル型にしたくなります
おいでさんも言及されている通り、「登録」「変更」というキーワードから、まずはいわゆるCRUD、Create(作成)、Refer(参照)、Update(更新)、Delete(削除)での整理をしたくなりますね。

仕様2・3を確認するためのテストケースは、こういうテーブルになります。単純な機能テストです。

作成 〇: ユーザ作成時に、ユーザ名をつけられる
参照 〇: 作成・変更したユーザ名を参照できる
変更 〇: ユーザ作成後に、ユーザ名を変更できる
削除 〇: 作成・変更したユーザを削除できる

次に、仕様4を考慮しましょう。横軸に、「使用されているかどうか」の要素を加えます。

使用されていないユーザ名 使用されているユーザ名
作成 〇: ユーザ作成時に、使用されていないユーザ名をつけられる ×: 使用されていないユーザ名を指定したユーザを作成できない
参照 〇: 作成・変更したユーザ名を参照できる n/a: 作成・変更できないので対象外
変更 〇: 使用されていないユーザ名に変更できる ×: 使用されていないユーザ名に変更できない
削除 〇: 作成・変更したユーザを削除できる n/a: 作成・変更できないので対象外

ここでもしかすると、「使用されている」の特殊パターンとして、「自分自身が使用している」というケースを思いつくかもしれません。言い換えると「ユーザ名の変更において、現在と同じユーザ名を指定する」ものです。この結果は、

  1. 問題なく「変更」処理を完了させる
  2. 「変更前と後で同じユーザ名である」旨のエラーメッセージを出す
  3. 「ユーザ名が重複している」旨のエラーメッセージを出す

といくつか考えられるので、仕様を確認するいい機会かもしれません。テスト設計はできるだけ早く始めましょう。なお、わたしは2.が好きです。

テスト設計2: ユーザ名の状態の分解能を上げる

おいでさんのツイの3つ目

  1. Aを削除してから別の人がAを登録しようとする

も大事ですよね。ですが、上の表の中にはうまく表現できていません。
このような場合、思いついたケースを単品で追加するのではなく、そのケースがどのような観点に属するものなのか考えることが大切です。

表をよく見ると、「使用されていない」には2つの状態が混在しているることに気づきます。

  1. 過去から現在まで一度も使用されていない
  2. 過去に使用されていたが、現在は使用されていない

「使用されていない」状態の分解能を上げると、テーブルはこのようになります。

一度も使用されていないユーザ名 現在使用されているユーザ名 過去に使用されていたが今は使用されていないユーザ名
作成 〇: ユーザ作成時に、一度も使用されていないユーザ名をつけられる ×: 現在使われているユーザ名を指定したユーザを作成できない ?
参照 〇: 作成・変更したユーザ名を参照できる n/a: 作成・変更できないので対象外 ?
変更 〇: 一度も使用されていないユーザ名に変更できる ×: 現在使われているユーザ名に変更できない ?
削除 〇: 作成・変更したユーザを削除できる n/a: 作成・変更できないので対象外 ?

最初に示した仕様記述の曖昧な点が明らかになりました。ユーザ名の一意性を「ある時点では一意」と考えるのか、「過去・未来に渡って一意」と考えるのかは重要な決め事なので、仕様をクリアにするいいチャンスです。

ユーザ名の使い回しがトラブルになりうるようなケースでは「過去・未来に渡って一意」とするのが良いのでしょうが、ここでは「ある時点では一意」、つまり「使い回し可能」ケースで表を埋めてみます。

一度も使用されていないユーザ名 現在使用されているユーザ名 過去に使用されていたが今は使用されていないユーザ名
作成 〇: ユーザ作成時に、使用されていないユーザ名をつけられる ×: 現在使われているユーザ名を指定したユーザを作成できない 〇: ユーザ作成時に、過去に使用されていたが今は使用されていないユーザ名をつけられる
参照 〇: 作成・変更したユーザ名を参照できる n/a: 作成・変更できないので対象外 〇: 作成・変更したユーザ名を参照できる
変更 〇: 使用されていないユーザ名に変更できる ×: 現在使われているユーザ名に変更できない 〇: 過去に使用されていたが今は使用されていないユーザ名に変更できる
削除 〇: 作成・変更したユーザを削除できる n/a: 作成・変更できないので対象外 〇: 作成・変更したユーザを削除できる

ここでも、「過去に使っていた自分のユーザ名を、再度使う」というケースを考慮してもいいかもしれません。

テスト設計3: ユーザ名のライフサイクルを考える

さて、ユーザ名の「状態」を考えた以上、状態遷移も考えたくなります。ユーザ名が生成されてから消滅するまで、これはユーザ名の「ライフサイクル」ということができるでしょう。

状態は、以下の3つでした。

  1. 過去から現在まで一度も使用されていない
  2. 利用されている
  3. 過去に使用されていたが、現在は使用されていない

ユーザ名のライフサイクルには、以下の5つがあります。

  1. 初期状態→1→終了状態: 一度も使われることのなかったユーザ名
  2. 初期状態→1→2→終了状態: 一度だけ使われたユーザ名
  3. 初期状態→1→2→3→終了状態: 一度使われた後、使われなくなったユーザ名
  4. 初期状態→1→2(→3→2)n→終了状態: 使い回されたユーザ名
     ※nは繰り返しの数。n>0とする。n=0はサイクル2と同じ。
  5. 初期状態→1(→2→3)n→終了状態: 使い回された後、使われなくなったユーザ名
     ※nは繰り返しの数。n>1とする。n=0はサイクル1と、n=1はサイクル3と同じ。

上のCRUDとは別に、このライフサイクルのテストもしたくなります。といっても、内容は重複しています。
サイクルaはテストするまでもないでしょう。サイクルbとサイクルc、サイクルdのn=1もカバーされていると言えるでしょう。

サイクルdとeを区別したテストは必要なさそうですが、サイクルdのn>1、サイクルeのn>2の、「複数回の使い回し」が起こるパターンはどう考えればよいでしょう。
これはもう仕様と実装によるので唯一解はなさそうですが、一般的にはこういうテストを行いたくなります。

  • n=3、つまり「使い回しが2回起こる」パターン。
  • n=大きな数、つまり「使い回されまくる」パターン。優先度低。

言いたかったこと

「一意の値」パターンのテストについて、「これが鉄板でござい!」と言いたいわけでないです。
ある仕様に対してテストケースを思いついてたら、それをポイっと追加する前に、

  • そのケースは、どういう観点の中に位置付けられてられるのか
  • その観点には、他にどのようなケースがあるだろうか
  • その観点を網羅するにはどうしたらいいだろうか

を考えるのがいいなあというのが、実は今回の主張だったりします。

単純な例ですが、「仕様で制限された文字数より長いテキストを入れたらどうなるんだろう」と思いついたら、

  • これは「文字列長」というテスト観点に属するテストケースだな
  • このテスト観点には、「短いテキスト」というテストケースもあるな
  • じゃあ、何文字のテストをやれば、この「文字列長」という観点を網羅できるだろう。同値分割を使ってみるか。

みたいな感じです。

サマリ (再掲)

  • ありがちな仕様なのに、テスト設計の定石がそこまで広まってないものがある気がする。
  • 「一意な値」パターンは、CRUDとライフサイクルの2つを考えるとよさそう。
  • テスト設計は、仕様の曖昧さを減らすことのできる活動である。早く始めよう。
  • 思いついたケースを無造作に追加していくのではなく、そのケースからテスト観点を逆導出すると、網羅性が考えやすくなる。

今回「一意な値」パターンと名付けてみましたが、このように「ありがちな仕様」をパターンとして抽象化し、それに対するテスト設計の定石をまとめるって、もしかするとすごく面白そうじゃないですか?
もうそういうのありますかね?

Generated By Dalle

「品質」と「価値」と「顧客満足」の関係が難しい

JaSST'24 Tokyoに参加して感じたことの一つに、「顧客に素早く価値を届けるために・・」という表現の発生頻度の高さがあります。特定の発表を批判したり、揶揄したりする意図はありません。この表現が決まり文句的に使われているために、その言葉の意味を考えずにいることを自覚したのでした。

「品質」と「価値」は何が違うのか。
みなさんからいただいたコメントも踏まえて、少し考えてみました。

先にイイワケしておくと、

  1. 過去の偉人の議論とか、論文とか、規格における定義とかはほぼ参照していません
  2. 特に明確な結論もありません、頭の体操にすぎません

ので、ご承知おきください。もちろん、「価値という言葉を使うな」という主張でもありません

1. 価値・品質 同一説

過去幾度となく引用されたであろうG. M. Weinbergの定義、

品質は誰かにとっての価値である。

を単純に受け取ると、「品質=価値」とも読めます。

身も蓋もない言い方をすると、2つの言葉に明確な意味の差はない。ないけれど、何となく目新しい響きもあり、好んで使われるようになったという説です。

面白いなと思ったのが山崎先生のこのご意見。

確かに、テスト・品質保証という言葉に、「守り」とか「最後の門番」の印象を持つ人も多いでしょう*1。極端にいうと、「マイナスをゼロに持ってくる」イメージ。
一方で「価値」というと、より積極的に「ゼロをプラスに変えていく」というイメージを、わたし自身は感じます。その結果、よりポジティブな印象を与える「価値」が、好んで使われるようになったというのはあるかもしれません。

2. 価値・品質 包含説

品質は価値の一部分ではあるけれど、そのすべてではない、という捉え方です。

秋山さんは例として

牛丼屋の、早い・安い・うまいの「早い」は価値だと思うけど、品質(商品やサービスの特性)ではないと思います。

とおっしゃっています。わたしは「早い」はサービス品質の一つだと解釈するので、ここは意見の分かれるところです。

ではわたしは、品質以外で何を、牛丼屋の価値と考えるのか。たとえばですが、

  • いつ行っても、期待した通りの品質のモノが供されるだろうという信頼感
  • どんな時刻でも、行けばご飯が食べられるだろうという安心感

があるでしょうか。うーん、これもサービス品質ですかね?

またJaSST後の懇親会で、とあるサービスのベンダとそのユーザ、お二人の会話を聞いていて感じたのが、
「このベンダは、自分たちユーザの要望をバックログリストに載せている」 という「実感」
もまた、価値の一つであるということです。これはサービス自体の品質でも、サポート自体の品質でもないのですが、ユーザを惹きつける要因だと思います。

別の例としては、一部のクレジットカードの、「持っていること自体がステータスになる」という特性は、クレジットカード自体の品質とは無関係の「価値」であると感じます。持ちたいとは思いませんが。

3. 価値・品質 因果説

品質が価値につながる、という解釈です。

身近な例だと、果物の「糖度の高さ」は品質で、「甘くてうまい!」が価値、ですかね。
「品質が価値につながり、その価値が顧客満足につながる」というのは、わかりやすい流れに思えます。

ただし、以下の3点に注意する必要があります。

同じ品質でも、ユーザが感じる価値は異なる

果物に「甘い」を求めていない消費者にとっては、糖度が高くても「うまい!」とはならないですよね。
品質が同じでも、価値が同じとは限らない。「開発側がユーザ目線でテスト」するにも限界があり、最終的にはユーザに聞くしかない、となります。

すべての品質が価値につながるわけではない

牛丼屋の例でいうと、食器をメチャクチャ高級なものにしたところで、顧客価値は上がらないでしょう。

ソフトウェアでいうと、たとえばソースコードの複雑度を下げれば、いわゆる内部品質が上がったことになりますが、顧客の価値が直接上がるわけではないでしょう。
もちろん、コードの複雑度が下がる→保守性が上がる→コードの変更の難易度が下がる→新規機能やバグフィックスがより容易に行える→ユーザに新機能やバグフィックス版を迅速に提供できる→ユーザは嬉しい という間接的な因果関係はあるかもしれません。

価値につながるのは品質だけではない

これは包含説と同じ話ですね。
ただ、「品質以外で価値につながるもの」が、わたしには今のところ「プロダクトやその提供者への信頼感」「ブランド」みたいなものしか思いついておらず、もう少し考えたいところです。

みなさんはこれらの3つのうちどれが、品質と価値の関係に近いと思いますか?

アジャイル・スクラムの原点では?

『アジャイルソフトウェア開発宣言』にも「価値」という言葉が出てきますが、これは顧客にとっての価値というより、ソフトウェア開発における価値、と読みとれます。
『スクラムガイド』(2020年11月版、PDF)には「価値」という言葉が何度も出てきますが、その一部は顧客にとっての価値を表し、他のものはスクラムチームにとって価値を表しているようにわたしには読めます。

違うかな?
この辺はあまり書くとボロが出るので(もう出てるか)、この辺にしておきます。

Weinbergに戻る

Weinbergに戻りましょう。
上述のWeinbergの言葉は、以下のように続くそうです。

品質は誰かにとっての価値である。
「価値」という言葉は、「人びとはその要求がみたされるなら、喜んで対価を支払う(または何かをする)か?」ということを意味している。

こうしてみると、「価値」とはシンプルに、「要求が満たされること」なんですよね。

でもその「要求」は、「人びと」が明確に言語化できるとは限りませんし、仮にできたとしてもそれは不変ではありません。なのでアジャイル開発では、高い頻度で「動くソフトウェア」をリリースして、「要求が満たされること」を確認するんですよね。

なので、「顧客に素早く価値を届ける」というのは、本当は、「顧客に素早く価値(があると自分たちが仮説として考えたもの)を届ける」なのかなーと思ったりもします。揚げ足取りかもしれませんが。

おわりに

もともと「品質っていったい何なんだ・・?」とQAエンジニアみんなが悩んでいたところに、「価値」という言葉も加わり、ますますワカンネー感が広がりました!

Generated By Dalle

追記

その後まとめたワンページサマリーです。

品質と価値の関係 ワンページサマリー

*1:その割には「QAエンジニア」というロールの認知度と印象が急に上がった現象もあり、不思議ではあります・・・。