要件定義と受け入れ条件
要求の曖昧さを具体的な入力・期待結果・測定条件へ置き換え、試験結果から判断できる範囲を説明します。
説明
開発の依頼には「分かりやすく」「一定額以上」「速く」のような表現が含まれます。そのままでは、作る側と利用する側で完成の判断が食い違うことがあります。受入条件は、どの条件で何が起きれば要求を満たしたと判断するかを具体的に記したものです。
通販の送料見積りを題材に、依頼文、金額の境界、例外、性能の測定条件を順に確認します。用意されたPythonの料金計算を実行し、5000円ちょうどで起きる不具合を試験で検出して修正します。資料とコードは同じ作業ディレクトリにあります。
要求の曖昧さを具体的な入力・期待結果・測定条件へ置き換え、試験結果から判断できる範囲を説明します。
1依頼文で決まっていないこと
最初の依頼文を読みます。「一定額」の値、「商品代」の範囲、「速い」とする条件は、まだ決まっていません。
cat request.txt依頼文を表示するこの文は利用者の希望を知る手掛かりですが、実装と受入試験を始めるには情報が足りません。疑問点を質問し、対象外の配送や負の金額の扱いも合意する必要があります。
2対象・金額・例外を明記
確認後の要求案を読みます。送料を求める機能と、結果表示にかかる時間を別のIDで管理しています。
cat requirements.txt要求案と未確定事項を表示する金額の基準は値引き後・送料を除く商品代です。機能の条件は具体化されていますが、性能は試験環境と要求間隔が未確定です。数値を書いただけで要求の合意が完了するわけではありません。
3境界の前後を受入例にする
5000円以上には5000円ちょうども含まれます。境界の直前・境界・直後に加え、0円と負額を試験データにします。
cat cases.csv入力と期待結果の組を表示する期待するのは送料の値、またはエラーです。条件分岐の書き方や変数名を指定していないので、別の実装でも同じ受入条件を使えます。
4料金を実際に計算する
現在の実装で三つの金額を計算します。出力のshippingを受入例のexpectedと対応させます。
python3 quote.py料金計算を起動する 4999 5000 5001境界の前後3件を入力する4999円は送料500円、5001円は無料ですが、5000円は送料500円になっています。境界を含めるという要求に、この実装は一致していません。
5受入例をまとめて実行
check.pyはCSVの入力を料金関数へ渡し、結果をexpectedと比較します。負の金額でValueErrorが発生した場合はerrorとして比較します。
python3 check.py cases.csvCSVの全ケースを実行するT-EDGEだけがFAILになります。手元の数件が成功していても、要求を満たしたとは限りません。境界の取り違えを検出できる入力が必要です。
6実装と修正案の差を見る
修正案では比較演算子だけを変えています。diffの先頭の-は元の行、+は修正後の行です。
diff -u前後の文脈を付けて差分を表示する -L quote.py -L quote-fixed.py quote.py quote-fixed.py> 5000を>= 5000へ変えると境界を含みます。要求が指定するのは5000円で無料になることです。この条件式はそれを実現する実装方法の一つです。
7修正後に同じ条件で再試験
修正案を作業ファイルへ反映し、期待値を変えずに再試験します。
cp quote-fixed.py quote.py修正案を作業ファイルへ反映する
python3 check.py cases.csv同じ受入例を再実行する5件すべてがPASSになります。修正対象の境界だけでなく、0円・負額など他の受入例も維持されていることを確認できました。
8性能の試験記録を読む
次に結果表示の速度を扱います。p95は応答時間を小さい順に並べたときの95パーセンタイルです。平均だけでは遅い応答の様子が分からないため、ここではp95を条件にしています。
cat measurements.csv人数・実施時間・応答時間の記録を表示するAは180msですが、利用者1人・60秒の試験です。要求案の100人・10分とは違います。Bの980msとCの1120msは閾値の両側ですが、先に試験条件を照合します。
9合否の前に測定条件をそろえる
試験の補足記録を読みます。要求を送る間隔が短くなると、同じ人数でも負荷が変わります。
cat measurement-notes.txt各試験の環境と負荷条件を確認するBとCは要求間隔が異なります。どちらを受入条件に使うか未合意のまま、都合のよいBだけを採用できません。性能要求の条件を確定してから、該当する環境・負荷で測定し、入力と記録を保存します。
まとめ
要求には対象、入力の意味、境界、例外、測定条件を具体的に記します。
受入例の期待値は実装と別に決めます。5000円の試験が比較演算子の誤りを検出しました。
機能試験が成功しても、性能や利用者の使いやすさを確認したことにはなりません。目的に対応する確認を別に用意します。
操作履歴と解説を振り返れます。
シェル / Python 3
cat request.txt買物の送料を分かりやすくしてほしい。 一定額以上なら無料にする。会計結果は速く表示する。
cat requirements.txt目的: 注文前に商品代と送料の総額を確認できること。 対象: 国内の通常配送。離島・海外・冷蔵配送はこの機能の対象外。 R-AMOUNT: 値引き後の商品代を整数の円で受け付ける。送料は含めない。 R-SHIP: 商品代が5000円以上なら送料0円、0円以上5000円未満なら500円。 R-INVALID: 商品代が負の値なら見積りを返さずエラーにする。 R-SPEED: 指定環境・100同時利用者・10分間の試験で、要求開始から結果表示までのp95を1000ms以下にする。 未確定: 本番相当の試験環境と要求間隔。性能要件の確定前に合意が必要。
cat cases.csvid,subtotal,expected T-ZERO,0,500 T-BELOW,4999,500 T-EDGE,5000,0 T-ABOVE,5001,0 T-INVALID,-1,error
python3 quote.py 4999 5000 5001subtotal=4999 shipping=500 total=5499 subtotal=5000 shipping=500 total=5500 subtotal=5001 shipping=0 total=5001
python3 check.py cases.csvT-ZERO input=0 expected=500 actual=500 PASS T-BELOW input=4999 expected=500 actual=500 PASS T-EDGE input=5000 expected=0 actual=500 FAIL T-ABOVE input=5001 expected=0 actual=0 PASS T-INVALID input=-1 expected=error actual=error PASS failed=1
diff -u -L quote.py -L quote-fixed.py quote.py quote-fixed.py--- quote.py
+++ quote-fixed.py
@@ -3,7 +3,7 @@
def shipping(subtotal):
if subtotal < 0:
raise ValueError("subtotal must be nonnegative")
- return 0 if subtotal > 5000 else 500
+ return 0 if subtotal >= 5000 else 500
if __name__ == '__main__':
for value in sys.argv[1:]:cp quote-fixed.py quote.py
python3 check.py cases.csvT-ZERO input=0 expected=500 actual=500 PASS T-BELOW input=4999 expected=500 actual=500 PASS T-EDGE input=5000 expected=0 actual=0 PASS T-ABOVE input=5001 expected=0 actual=0 PASS T-INVALID input=-1 expected=error actual=error PASS failed=0
cat measurements.csvrun,users,duration_s,p95_ms A,1,60,180 B,100,600,980 C,100,600,1120
cat measurement-notes.txtA: 開発PC、利用者1人、要求間隔1秒。 B: 検証環境v2、利用者100人、要求間隔1秒。 C: 検証環境v2、利用者100人、要求間隔0.2秒。 要求間隔が合意されていないためB/Cを同一の受入試験とは扱わない。 集計値の計算手順と生データも、受入記録に添付する。
演習完了