TDDがうまくいかないときは、BDD形式でバックログを書いてみる


ラクスルに入ってはや2年を迎えたおっさんCTOの泉です。ラクスルに入ってから 6kg 体重が増え、ますますおっさんとしての安定感が増してきました。次の2年で 6kg 減らす予定です。

さて、今回のお題はエンジニアなら誰しも知っている有名な開発プラクティスであるTDDを実践する上でのTIPSです。

TDDはなかなか実践に至らなかったり、実践してみてもなかなかうまく行かず、挫折してきたエンジニアも多いのではないでしょうか。

ラクスルではXPでTDDを実践しはじめてからかれこれ1年近く経ちますが、なぜ今までTDDはうまくいかなかったのか、いままでとは何が決定的に違うのか、というのをそれとなく考えてみたところ、「バックログをBDD形式で書きはじめた」ことがTDDの実践に大きく影響を与えているのでは、と思うようになりました。

テストに入りやすいストーリーの書き方とは?

いざTDDでやってみよう、と思ったときに一番困るのが、「ストーリーに書かれている要件は理解したが、俺は何をテストすれば良いんだっけ?」と、テストケースそのものが想像できずに悩んでしまうことです。

例えば、「ユーザーは、届け先の住所を郵便番号検索で自動入力させることができる」というストーリーに対してテストケースを作れって言われても、一見シンプルそうなのにどんなテストケースを書けばよいのか、わかるようで正直いまいちわからない。

この時にAS/GIVEN/WHEN/THEN というBDD形式でストーリーを書くと、テストに簡単に入ることができます。(BDDはTDDの流儀の一つと考えると、当たり前っちゃ当たり前なんだが)

もともとこの書き方は、Pivotal Labsとの協業で学んだバックログの記述方法なのですが、この形式で定義されていると、圧倒的にテストに繋げやすい。

いままで自分は、BDDの定義は「エンジニアのお仕事」という理解だったのですが、プロダクトマネージャーが行うことで、エンジニアに要件がより正確に伝わり、結果エンジニアの考える工数を大幅に減らせることがわかりました。

書き方は以下の通り

  • AS – 誰が?
  • GIVEN – 前提条件: テストケースに入る前のシステムの状態は?
  • WHEN – どのような操作や入力があるのか?
  • THEN – その操作や入力のあとに期待すべき結果は?

ジュラン ロードスター NCEC 左用 ※北海道・沖縄・離島は送料都度確認 ※代引不可 SC3タイプ 05.08~14.05 [JURAN] SC3タイプ 左用 シートレール

ここではシステムを使うステークホルダーの名称を書きます。「エンドユーザー」「管理者」「未登録のビジター」等の役割でも良いです。より良いのはオペレーターの「佐藤さん」調達管理の「鈴木さん」ヘビーユーザーの「加藤さん」等、チームで定義したペルソナの名前を表記すると、その人に対する価値提供をよりイメージしやすくなるかと思います。

GIVEN(OPTIONAL)

これはテストの前提条件を表します。例えば、

  • ログインしている状態
  • 「仮受付」の注文データが既に登録されており、支払いの入力が済んでいる状態

等、ユーザーの操作が行われる前に、システムがどういう状態にあるのかを明確にします。

そうすることで、エンジニアがテストを書く際、ログイン状態を作っておいたり、それに必要なフィクスチャーを準備することができ、テストの書きやすさにダイレクトに効いてきます。

ちなみにGIVENは OPTIONAL と書きましたが、たまに「ログイン状態に決まってるだろ」みたいに冗長になることが多いので、前提が書かずとも明確な場合は省いても良いと思ってます。

WHEN

ASで指定したユーザーはどのような操作を行うのか?平たく言えば、システムに対するインプットの定義を記述します。例えば、

  • 届け先フォームの「郵便番号」に「1060047」と入力すると
  • 「検索」ボタンを押すと

等。

かならずしも入力の伴わない行動もあります。例えば導線をクリックするとか。その場合は

  • AS ユーザーとして
  • GIVEN ログイン状態でマイページを表示しているとき
  • WHEN グローバルナビゲーションから「お問い合わせ」をクリックすると

といった形で、「結果をトリガーするアクション」を記述すれば良いのだと思います。

あるいは、バッチスクリプトなど、人間が行動を起こさない場合でも、「AS バッチ WHEN 9:15AMにバッチが起動すると」と記述することも可能です。

THEN

最後にその結果、何を期待するのかを記述します。例えば、

  • 「検索」のボタンが押下可能になる
  • 都道府県=「東京都」、市区町村=「港区南麻布」が自動入力される

等。これはテストにおいて、 assertion に表される部分です。

AND(OPTIONAL)

ちなみに、THENで期待することが二つになったり、アトミックな操作が後続する場合は、WHEN/THENを複数定義して、ANDを使って結合したりします。例えば、

AS ユーザーが
WHEN 届け先の郵便番号に106-0047と入力すると
THEN 「検索」ボタンが押下可能になり
AND
WHEN 自動記入のボタンを押下すると
THEN 都道府県=「東京都」、市区町村=「港区南麻布」が自動入力される AND 番地のフィールドにカーソルが移動する

等。ただし、上記のように、詰め込みすぎな案件が出来上がってしまうので、濫用するのはおすすめしません。このような形になるなら、「検索ボタンのステータス変更」「自動記入」と、2つのストーリーに分解するほうがよいかもしれません。

また分解することで「検索ボタンの押下可能制御は、ユーザビリティーの問題なので後回しにすっか」という意思決定もできるようになります。

実践例

実際、我々が運営する物流サービスの「ハコベル」のバックログを上記の書き方にして見ました。Before〜Afterで一例を見てみましょう。

この例は、いわゆるスマホアプリ内でみる「通知設定」的な機能です。

荷主様より新たな配送案件をお預かりする際に、ドライバーが使っているアプリケーションに新規案件が入ったことを知らせるためにプッシュ通知をしているのですが、

[JURAN] ジュラン シートレール 左用 SC3タイプ ロードスター NCEC 05.08~14.05 ※代引不可 ※北海道・沖縄・離島は送料都度確認【特価新作】


ウェッズ WedsSports REVSPEC PRAIMES フロント用 トヨタ スープラ JZA80 94/8~02/7 17インチ車(4ポットキャリパー)


[JURAN] ジュラン シートレール 左用 SC3タイプ ロードスター NCEC 05.08~14.05 ※代引不可 ※北海道・沖縄・離島は送料都度確認 215/50R17 FALKEN ファルケン ZIEX ZE914F ジークス ZE914F Kashina FV7 カシーナ FV7 サマータイヤホイール4本セット

↓↓↓ディクセル Specom-α ブレーキパッド フロント キャデラック STS 4.4 Supercharger 295V 2006年01月~2011年;FABULOUS AZR60G/65G ヴォクシー 後期 Rev2 DRIVING LAMP (クリア/ファブレスオリジナルロゴ入り);BRIDGESTONE EXEDRA MAX 180/70-15 M/C 76H TLブリヂストン・エクセドラマックス リア用※チューブレスタイプ 商品番号 MCS01303, ビラーゴ250(VIRAGO) サイドナンバーキット クロステールランプ ガレージT&F, 2輪 D.I.D VXシリーズ シールチェーン スチール 110L カワサキ GPZ400R 400cc 1985年~1989年, 【メーカー在庫あり】 ユーカナヤ U-KANAYA ビレットレバーセット GPタイプ ショート XJR1300、XJR1200 黒 YA011-004-0601 JP店, 18インチ サマータイヤ セット【クラウンロイヤル(200系 全グレード)】MANARAY ヴァーテックワン ロングビーク タンザナイトブルー/リムDC/アンダーカットポリッシュ 8.0Jx18Bluearth RV-02 225/45R18;スタッドレス ファルケン 16インチ 4本 195/60R16 89H エスピア ダブルエース WACE スタットレスタイヤ FALKEN ESPIA W-ACE ☆☆ダンロップ ウインターマックスSJ8 スタッドレスタイヤ 235/60R18 107Q XL 4本セット↓↓↓185/60R15 84H DUNLOP ダンロップ ENASAVE PREMIUM エナセーブ プレミアム TRG-SILBAHN TRG シルバーン サマータイヤホイール4本セット, 2輪 アクティブ グッドリッジ・ビルドアライン ブレーキホース フロント Wダイレクト/アルミ ホースカラー:クリアホース,スモークホース カワサキ Z1000R, ホイール: YOKOHAMA ADVAN Racing RGIII ホイールサイズ: 8.5J-19 & 9.5J-19 タイヤ銘柄: YOKOHAMA DNA ECOS ES300 タイヤサイズ: 235/35R19 & 265/30R19 タイヤ&ホイール4本セット【19インチ】, 【送料無料】 245/35R21 21インチ WORK ワーク グノーシスGR 204 9J 9.00-21 FALKEN ファルケン アゼニス FK453 サマータイヤ ホイール4本セット フジコーポレーション, フィット ハイブリッド GP5/GP6 エアロ 3点キットC / ( FRハーフタイプ )【ムゲン】フィット ハイブリッド GP5 Front Under Spoiler スタイリングセット メーカー塗装 2トーンカラード仕上げ ツヤ消しブラック塗装×ティンテッドシルバー・メタリック(NH823M)

ディクセル ブレーキローター PD(プレーンディスク) フロント左右セット ランサー/ランサーセディア C61A/C62A/C64A 3416031 取付セット DIXCEL ディスクローター ブレーキディスク【店頭受取対応商品】

↓↓↓↓↓↓


↓↓↓↓↓↓
↓↓↓↓↓↓

RS-R Ti2000 SUPER DOWN サスペンション フロント ミツビシ エアトレック, MEMPHIS SHADES メンフィスシェイズ スクリーン ウインドシールド ドロップトップ式 9” 17-20” ナイトシェード ブラック 【DROP TOP 9 NS 17-20 BLK [2313-0175]】, 18インチ サマータイヤ セット【適応車種:アルファード(20系)】HOT STUFF Gスピード P02 メタリックブラックポリッシュ 7.5Jx18LEMANS V LM5 235/50R18, [YAKIMA 正規品] Audi アウディ A6/S6 セダン C7系に適合 ベースラックセット (ベースライン・ベースクリップ110,136・ジェットストリームバーS), 【送料無料】【VW純正 ザ ビートル専用】ペダルセット 1K2064205A

【プロミュー】送料無料【project mu】三菱 ランサー RACING-N+ ブレーキパッド 前後セット CD9A ランサー エボリューション (92/10~94/1);16インチクラウンロイヤル180系 全グレードENKEI パフォーマンスライン PF03 マットダークガンメタリック 6.5Jx16トランパス mpZ 215/60R16 DIXCEL (ディクセル) フロント ブレーキローター PD 3212085 スカイライン PV35 02/03~06/11 GT-8 (CVT) 【割引クーポン配布中】ENDLESS/エンドレス MX72 イプサム ACM21W/26W H13.5~H21.12 リア 商品番号:EP385;【割引クーポン配布中】ENDLESS/エンドレス MX72 カローラ ZZE122 4輪ディスク H12.8~H18.9 フロント 商品番号:EP382

ラフ&ロード(ラフアンドロード) 秋冬モデル RR7660 デュアルテックスウォームパーカー [オリーブ/ブラック/M];CRIMSON(クリムソン) CLUB LINEA L612 (クラブリネア L612) 20インチ 9.5J PCD:120 穴数:5 inset:5 DISK TYPE:SUPER LOW カラー:ブラックサイドマシニング [ホイール1本単位]/H

、ドライバーの方が休暇を取られたりする際にも通知が届いてしまい、ノイズが多くなってしまったので、アプリケーション側で通知の設定を行えるようにしたい、という案件です。

これが元々のストーリーです。

題名:ドライバーは、プッシュ通知、メールの新着通知ON/OFF、ONの時間設定をすることができる

詳細:
・設定ページ上でプッシュ通知のON/OFF設定ができる
・設定ページ上でメール通知のON/OFF設定ができる
・ONの場合は00:00~00:00で30分間隔で通知設定を行うことができる
・OFFになっていても、案件は従来どおり開示される
・XXXXXXの場合は HKS SQV4ブローオフスカイラインGT-R SKYLINE GT-R BCNR33 RB26DETT 95/01-98/12 スカイラインGT-R SKYLINE GT-R BNR34 RB26DETT 99/01-02/08、XXXXXXを優先する (企業秘密❤)

これがBDD形式にすることで、このように書き換わりました。

題名:ドライバーは、プッシュ通知、メールの新着通知ON/OFF、ONの時間設定をすることができる

AS ドライバーとして
GIVEN ログインしている AND 案件一覧を表示しているとき
WHEN 設定画面の⚙アイコンをタップすると
THEN 設定内容が表示されデフォルト値が設定されている(ON)
like
| プッシュ通知 | *ON*/OFF |
| 通知時間設定 | ON/*OFF* |
| 通知時間設定 | 00:00 ~ 00:00 |
| メール通知 | *ON*/OFF |

主語はもともとストーリーの題名ではっきりしていたものの、一体どの画面で何を期待しているのか、がエンジニアからみてもかなりクリアになり工数付けがしやすくなります。

因みにこの例では 、 like 〜 (和訳:〜の様に)とありますが、このように表をつかったり画像を埋め込んだりして、どのようにそれが見えるのか、といった補足情報を入れる場合もあります。

元々の要件では、ドライバーが通知の設定画面を表示し、変更をDB反映させたり、実際プッシュ通知の制御をすることも同ストーリー内で定義されてました。

このストーリーは clazzio シートカバー クラッツィオツィールタイプ マツダ デミオ 型式 DEJFS 年式 H23/7-H26/9 グレード 13スカイアクティブ系 ≪ 1列目ヘッドレストサイド形状:楕円形車/リア背面6:4分割車用 ※実車ヘッドレストサイド形状確認必須 ≫、表示・保存、さらに設定を適用した通知の振る舞いを変える、さらに(企業秘密❤)と4ストーリーに分解されました。この分解を行ったあと、元の要件に記述されていた(企業秘密❤)については、まずは、上記3点をリリースしてみて、その後に考えよう、ということになりました。つまり3つさえ終われば、「無駄な通知は届かない」という価値提供ができ、4点目の実装をまたずに先にリリースすることができるのです。

これくらい明確になれば、Request Spec で、設定ページにアクセス→デフォルト値が設定されている、というテストをすんなり実装できそうです。その後、実装に入り、テストをパスさせることだけに集中すれば、最小工数で実装を終わらせることができます。

TDDってなんでやるんだっけ?

TDDの目的には、テストカバレッジが上げることや、リファクタしやすさ [Rigid Industries 正規品] E-シリーズ PRO 6” LEDライト LEDカラー:ホワイト 発光パターン:ディフューズ、将来の変更に対する保険といった見方もあると思いますが、もっと本質的なメリットとしては「無駄な開発をしない」という点かと思います。

実装をしていると、例えば「あ、ここは直しておきたいな」「こういうUIの気遣いがあるとユーザー喜ぶんじゃないか」など、ついつい「やっておこうかな、やっておきたいな」という衝動が湧いてきます。このような「ムラムラ感」に対して「いや、やめておこう。いまは、このテストをPASSさせることだけに集中しないと」と抑制が効いてきます。

ぐっとこらえる!

もちろん、そういったムラムラ感は大事です。でも カーマット フロアマット スバル サンバートラック 26年9~ AT-LUXブラック、事業的には、今開発していることは他にやりたいことを犠牲にして優先順位を上げてやっているわけで、その開発に対する投資(つまり開発の時間的な投資)は最小限、すくなくとも計画通りにしておきたいとも思うでしょう。

同じ価値提供をしているのに、工数が2倍に膨れ上がってしまっては「そんなに時間がかかるのであれば他のことをすればよかった」とその投資の正当性が崩れることもあります。開発をTDDで行うと、テストを通すことを最優先にするため、必要最低限の開発に留めることが可能になるのではないでしょうか。

ここはぐっとこらえながら、そのリファクタリングのアイディアは、次のHack-It Day向けに貯めておきたいところです。(*Hack-It Dayは月に一度、ラクスルのエンジニアが自由に開発することができる日)

結論

さて、最後の方はちょっと脱線しましたが、ここまで開発スコープが明示化されていると、少なくともRequest Specを書くことは圧倒的に容易になりテストドリブンの実践がかなりラクになります。

「TDDが出来ないのは俺(エンジニア)が悪いんじゃない!プロダクトマネージャーが悪かったんだ!」って言いたい訳ではないが、「要件ってどうやってエンジニアに伝えればいいんだろう?」と実際悩むプロダクトマネージャー(PM)の方も居ると思うので、この手法はオススメです。

半面、実際に書いてみると意外と難しい作業です。この粒度で要件を定義するためにはそれなりに深く考える必要があります。主語を特定すること、システムにどのような前提があるのか 【ハイエース200系 タイヤ ホイール 新品 4本セット】◆RAYS TBR TB01 レイズ トゥブラザースレーシング TB01◆225/50R18新品TOYO トーヨー TRANPATH トランパス MPZ 【バランス調整済み!】、何をインプットすると何がアウトプットとして出てくるか。慣れないとなかなかチャレンジングな作業だと思います。

しかしPMが「自分が実現させたいことをもう一度整理してみよう」というきっかけにもなりますし、開発が終わった段階で受入テストをする真面目なPMであればその手順が明確なので、スムーズにテストをすることができるかと思います。

また副次的な利点としては、上の例にあるように自然に「分解」されることかと思います。大雑把な要件からこの書き方に変えると、自然に「1ストーリー1アクター1要件」に絞られてくるので、「あ、これだったらもう一つストーリー切らないと」といった感じに、分解が進みます。

それによって「一旦こっちを優先しよう」「これは後回しでいいや」「お、一旦ここまで終わってれば機能リリースできるじゃん」と、より俊敏に動くことができそうです。この「柔軟性」がオプションとして後々選択肢に加わるのであれば、もう少し頑張って定義する価値もあると思います!