スクラムマスターの資格って必要?本当に答えます

スクラムマスターに資格(職務に就くための必須の条件)はありませんが、認定(スクラムマスターとして有能である、十分である)はいくつかあります。

ですので、タイトルに直接答えれば「資格は必要ありません(そんなものはありません)」となります。

いっぽうで、スクラムマスターの認定はいくつかの組織から提供されています。こちらは勉強する目標としても、就任する上での判断材料のひとつとしても、有用ではないかと思います。

Certified Scrum Master® / CSM® / 認定スクラムマスター

非営利組織Scrum Allianceが提供しています。認定トレーナーの研修を受けた上で受験します。日本ではOdd-e Japanアギレルゴコンサルティングアトラクタなどが提供しています。

Licensed Scrum Master / LSM

Scrum, Inc.が提供、日本ではScrum, Inc. Japanが提供しています。研修を受けた上で受験します。

なお、Scrum, Inc.(米国)では現在 Licensed Scrum Master という名称を使わず、 Scrum Master by Scrum Inc.™となっているようです。Scrum, Inc. JapanでもLSMという表記が目立たなくなっており、名称の変更がされるのかもしれません。(2021年5月現在)

Professional Scrum Master™ / PSM

Scrum.orgが提供しています。試験だけ合格すれば認定を取得できます。対応する研修もあり、日本ではITプレナーズなどが提供しています。

スクラムマスター以外

それぞれ、より上位の認定や、スクラムマスター以外のプロダクトオーナーや開発者の認定も提供しています。より広く・深く学ぶうえで有用だと思います。

参考にした資料:

なお本記事はCC-BY 4.0で提供します。ご自分のブログ、メディア、サイトなででそのまま掲載いただいて構いません。

こちらの講演時にいただいた質問への回答です。

「テスト自動化とテスト駆動開発」講演資料 - やっとむでぽん

質問10. 仕様変更時、テストが壊れるケースがあると思います。どのようにプログラム修正するのが良いでしょうか?リファクタリングとは異なり、新規開発のように先にテストを書くのでしょうか?

スライド88で、機能追加のプロセスを説明しました。仕様変更も機能追加と、手順は変わりません。

f:id:yach:20210409141139p:plain

仕様変更は「動作を変える」ことなので、リファクタリングとは違うものです。リファクタリングは動作を変えないままで、内部構造を変更します。余談ですが、リファクタリングを単なる「ソフトウェアの変更」という意味で使う場面を見るのですが、本来の意味とは違います。

この図の「自動テストがある場合」では、実装を終えてから、変更後の仕様に合わせてテストをまとめて変更します。すべてのテストが成功したら、仕様変更は完了です(もちろん見落としがなければです)。 同様に「テスト駆動開発の場合」では、まず変更後の仕様に合わせてテストを直します。ただし、TDDのサイクルは小さいので(数分~数十分)、すべての仕様変更を一気に盛り込めません。どうするかというと、仕様変更をブレークダウンして、テストと実装がすぐ終えられるように小さくしていきます。そのうえで、ひとつずつをテスト駆動開発で進めていきます。

たとえば、ユーザーがデータを操作する画面(ブラウザ)があって、そこに項目を1個増やすような仕様変更を考えてみます。ブレークダウンした結果は、たとえば次のようになります(あくまで一例です)。

  • 入力画面に項目を増やす
  • テーブルに入力項目に対応するカラムを増やす
  • 保存処理で新しい項目を保存する
  • 表示画面に項目を増やす
  • 入力画面のバリデーションを直す
  • 入力内容確認画面に項目を増やす

このようにすると、ソフトウェアは「動作するきれいなコード」を保ちながら、機能としては中途半端な状態を経由して、少しずつ完成に近づいていきます。入力はできるけど保存されない、保存されてるけど表示できない…それぞれのステップでテストを書き足して(書き換えて)、実装し、リファクタリングまでします。

このように進めるのは、ふわっとした言い方ですが「安全」のため、ないしは不安が小さいままで進めるためです。一気にテストを直すと、大量にテストが失敗する状態になります。すべてテストが成功するのは、入力し、保存し、表示ができて、バリデーションまで…それまでの間、テストが成功しているから壊れていない、という状態がおあずけになります。壊れていない保証がないまま直すのは、不安です。途中で思うようにいかなくなって、あれこれ試行錯誤すれば、不安はさらに増大していきます。

テストが成功している状態は、「動作するきれいなコード」なので、安全エリアです。不安になったり、先に進むやり方がわからなくなったら、いったん安全エリアまで戻り、何も壊していない状態を確認してから、あらためて次の一歩の作戦を考えます。この安全エリアにいつでもすぐ戻れるように、仕様を少しずつテストにして、実装していくのです。

不安の程度、安全エリアからの距離というのは、多分に心理的なものです。とりわけ、担当者の理解度、自信、体調、疲労度、言語の練度などによって大きく変わります。スキルのある人が元気で頭がフル回転している月曜日であれば、もしかしたら一気にすべて書き上げてしまえるかもしれません。自信を持って「大きな一歩」で飛び越えられるなら、それに越したことはありません。しかしそこまでスキルのない人、このコードを初めて見るメンバー、疲れ果ててる金曜日、今夜の試合が楽しみで気もそぞろ……というような場合は、もっと小さな一歩ずつにブレークダウンしたほうが安全になります。

和田卓人さんは、こうしたスキルや状況に応じて一歩の大きさを変えることを「間合いを調整する」と表現しています。自信があるなら間合いを広くとって、一発で決める。自信がなければ間合いを狭くして、細かく密接に追随する。テスト駆動開発のメリットとは、そうした間合いを自由自在に調整して最適な仕事の進め方を選べるように、開発者がなる、そういう能力を獲得する、と言うこともできます。

質問11. カバレッジを上げようとすると費用対効果が悪くなっていくという理解です。どこまでテストすべきか指針となるものはありますか? 例.業務ロジックのみテストコードを書く…など

前の質問への回答と関連しますが、和田卓人さんはテスト駆動開発においては「不安を手がかりにしてテストを書きます」と言っています。プログラマーは自分の心の声を聞いて、自分が対象のコードをどのくらい自信を持って理解しているか把握します。その心の声が「ちょっとヤバイかも」と囁くなら、テストを書いて確認する。というのが、とても曖昧に聞こえますが、現場の方へのアドバイスになります。

自明ではありますが、大事なところは丁寧にテストします。お金を扱う、SoRで保存すべきデータを保存する、個人に紐付く情報(個人情報に限らず)、複雑なビジネスロジック、などは仕様の検証、設計のレビューなども含めて手厚くテストをします。テストを自動化する場合でも、どういうテスト設計をしたのか、それぞれのテストケースにどんな根拠があるのか、あとからわかるようにしておいたほうがいいでしょう。

自分たちのコードでない部分も、場合によってはテストを厚くします。外部からデータを取り込む場合(外部にどんなバグがあるかわからない)、ライブラリを使う場合(ライブラリを作った人をどれだけ信用するか、ライブラリのバージョンアップで急に挙動が変わったら気づけるか)。

こうしたアプローチは、リスクベースドテストの一種とも言えます。危険そうなのはどこか、問題があったとき損害が大きくなるのはどこか、自分たちが把握しきっていないのはどこか、そうした観点を用います。リスクベースドテストについて、秋山浩一さんのブログを引用して紹介しておきます。(秋山さんは、スライド内で紹介した『ソフトウェアテスト技法ドリル』の著者。)

リスクベースドテストとは

『ISTQBテスト技術者資格制度 Foundation Level シラバス 日本語版 Version 2018.J03』では、「リスクベースドテスト」をテスト戦略のひとつ(分析的テスト戦略)に位置付けています。該当部分を引用します。

テスト戦略は、通常、プロダクトまたは組織のレベルでの、テストプロセスに関する汎用的な考え方を提供する。テスト戦略の一般的な種類は以下の通りである。

・ 分析的:いくつかの要因(要件やリスクなど)の分析に基づく。分析的アプローチの例としては、リスクのレベルに基づいてテストを設計し優先度付けするリスクベースドテストがある。

さらに「5.5.3 リスクベースドテストとプロダクト品質」の項で詳細な説明があります。こちらは、ちょっと長いので要約しています。(オリジナルが気になる方は、JSTQB FLシラバスの71-72ページをご一読ください。)

リスクを使用して「テストの内容・優先順位・実施順番を決めること」ができる。また、テストを「プロダクトリスクを減らし残存リスクを明らかにすることを目的とした活動」と位置付けることができる。
識別されたリスクに対して、(期待しない事象が)「発生する可能性」と「発生した場合の影響」を評価する。

(残存リスクに対して)リスクマネジメントで実施することは下記の通り。
・ リスクを分析する(定期的に再評価する)。
・ コンティンジェンシープラン(緊急対応計画)を作る。

note.com

コードカバレッジの数値は、手を入れるべき場所を見つけるために使うとよいです。達成目標にすると、機能不全を起こします。全体が80%だからOKとかNGとかいうのではなく、特にカバレッジの低いところがないか、ドリルダウンしていってテストが足りていないところを見つけるという使い方がよいです。

仕様に対するカバレッジも、テストの不足を見つけるのに使えます。コードカバレッジのように自動で測定するのは難しいかもしれませんが、仕様ドキュメントに対するテストのカバレッジや、仕様としてのビジネスロジックの場合分けに対するカバレッジ、画面上の操作に対するカバレッジなどを調べます。

そういう意味で、どういうテストを書くべきかという基準はあったほうがよいでしょう。詳細な定義やルールではなく、プログラマーを導けるようなものにするとよいと思います。これは思いつきですが、

  • ユーザーのやりそうな操作はすべてカバーする
  • 外部データ入力は創意工夫して変なパターンをテストする(もちろん普通のケースはちゃんとやる)
  • 異常なパラメータだったら異常な結果になるかテストする
  • 境界値、同値クラスを意識した上で、変なパラメータを選ぶ

のようなものです。○×を付けるようなものではなく、「どうしてこのテストがいる/いらないと思ったのか?」と議論できるようなものだとよいのではないでしょうか。(すみません、この部分は本当に思いつきなので、あまり信用されないでください。面白そうだったら試してみてください。)

10年前くらいのJavaの経験だと、コードカバレッジ(C0)は80~90%くらいが良い感じで、それより低かったら、なにか抜けていそうなので調べるというふうにしていました。95%以上にするには、テストが大変になったり、無駄に設計を変えないといけなかったり、現実には絶対起きない例外をおこしたりしないといけないので、やらないようにしていました。

コードカバレッジでは検出できない抜け漏れもあります。数字だけ意識していると、こうした部分のテストを見落とします。

こちらの講演時にいただいた質問への回答です。

「テスト自動化とテスト駆動開発」講演資料 - やっとむでぽん

質問9. DBアクセスを含む処理をテストする場合、どのように環境を整えるのが良いと思いますか? 例.開発環境DBにつなぐ/クライアント端末にDBを建てる/メモリ内で完結させる

挙げていただいた例は、いずれも有用で、使い分けるとよいです。使い道を分類すると、どれも使うことになるのではないかと思います。使い道、目的に合わせどれを選ぶか、どう運用するか、私の経験から考えてみます。

まずテストの実行時間です。テスト駆動開発のサイクルで使うテストは一瞬(1秒以内)に結果が見たいので、本物のDBは避ける傾向にあります。DBアクセスする部分をモックで置き換えてやれば、実行時間は本当に一瞬(0.1秒以内)になります。これがCI/CD環境で実行するテストであればもっと遅くても構わないので、テスト用のDBに接続して実際のDBアクセスをします。もっとも今どきの環境は、クライアント端末にローカルのオンメモリDBを立てても、実行速度には遜色ないでしょう。フレームワークがそういうテスト用機能を提供している場合もあります。モックの濫用はテストの保守性を下げるので、カジュアルにDBを使うのも選択肢になると思います。

テスト対象も大事です。DBアクセスを含む処理の、なにをテストしたいのか。SQLそのものなら、DB接続は必須です。複雑なクエリを組み立てる部分のテストなら、クライアント端末にオンメモリDBを立てて実行できます。本番準拠のデータからクエリでデータが正しく取得できるか確認するようなテストなら、テスト用DBにデータを準備するほうが経済的かもしれません。

いっぽう取得したデータに対して実行するロジックがテスト対象なら、DB接続はせず、取得したのと同じデータをテストケース内で作って、それを渡してやればすみます。こうした細かな部分を個別にテストを書いていると、ソフトウェアの設計が自然と細粒度になり、依存関係も分離できた状態になります。テストデータのバリエーションも、テストケース内のほうが細かく大量に作りやすいかもしれません。

テスト対象がエンドツーエンド、ユーザーが操作する画面や、バッチの入力ファイルと出力ファイルを対象とするテストならば、当然DB接続は必要です。パフォーマンスを見るなら本番同等のDB環境を使います。

以上のように、テストの種類によって接続するDBも変わってきます。どの環境でどの種類のテストを実行するか考えてみると、一例として以下のようになります。

  • 開発中(テスト駆動開発) ― 速いテストだけ、ユニットテストだけ実行する。DB接続しないか、ローカルDBを使う。
  • 開発の区切り(コミット時、プッシュ時、マージ時、など) ― 手元で実行できるテストをすべて実行し、壊していないか確認する。適当なデータがあるDBに接続するか、ローカルにそういうDBを(テスト時だけ)起動する。
  • CI/CD ― ビルド環境上ですべてのテストを(システムテストや受け入れテストも)実行する。テスト用に用意した、本番同等のRDBMSに接続する。
  • 手動テスト ― 開発中に動かして動作確認したりするときには、手動テスト環境を使う(専用環境でもいいし、ローカル環境も使える)。適当なデータが入っていないと使いにくい。開発中のプログラムから使うので、データが壊れたり異常になったりするのを想定して、運用する。テスト担当者も触るという場合は、さらに別の安定した環境が必要になることもある。

テストデータの問題も大きいです。以下のような作戦があります。

  • テストデータを詰め込んだ「テストDB」を用意し、そこに接続する。手動テストでは、使えるデータを見つけたり作ったりしてテストを実施できる。自動テストの場合、テスト内で変更したデータを再度テストに使うと、テストの事前条件が異なるので結果が変わってしまう。そのため、自動テストを実行するたびに初期テストデータをロードする必要がある。初期テストデータは、テストの内容や機能によって変わっていくので、ソースコードと同じくバージョン管理対象になる。開発中に手動で動作確認することもあるので、それ用の「開発DB」と、自動テスト用の「テストDB」は、別々に用意する。複数人が同時並行してテストを実行することを考えると、開発者1人にテストDBを1つ用意するほうが安全(DBMSとしては1つで、ユーザーや名前空間で分離してもよい)。
  • テストデータが「全テーブルの全レコード」で構成されていると、いろいろな条件でテストしたいときにバリエーションが足らなくなる。そのため、個々のテストケースについて、そのテストのためのテストデータを構築する。テストケース内でプログラムでデータを生成してもよいし、ExcelCSVで別ファイルとして用意しておく方法もある。プログラム内で生成すると、「どんなデータにどんなテストをするか」が一緒に書いてあって読みやすくなり、変更もしやすい。いっぽう、複数のテーブルに渡る複雑なデータ構造を組み立てるのに、プログラムでは煩雑になり過ぎることも多く、データを外出し(別ファイル)にするのもよい。
  • プログラムをテストコードから呼び出してデータを生成したい、しかし煩雑になる、という場合に、テストデータを構築するためのヘルパーライブラリを作るという方法もある。またRuby on Railsのfactory_botやLaravelのSeederのように、フレームワークサードパーティのライブラリとして、そうした機能が提供されているものもある。
  • 別の観点として、アジャイルではデータスキーマも頻繁に変更するため、DDLもバージョン管理対象とし、テスト実行時にバージョンを確認したり合わせたりしないといけない。フレームワークマイグレーションスキーマバージョン管理の機能を提供していることがある。

プロジェクトのテスト戦略として、どういう部分をどういうテストでカバーするか計画し、漏れがないようにしましょう。たとえばDAO(Data Access Object)を、ユニットテストでは「統合テストで確認するから」とモックで置き換え、統合テストでは「ユニットテストで確認してるから」と網羅しなかったりすると、テスト漏れになってしまいます。逆に、システムテストで「絶対確実だからここで全条件網羅する」などと考えると、テスト量も実行時感も膨大になり、費用対効果が悪くなります。

いろいろ書いて、整理ができておらずすみません。私がいいなと思う一例は、こんな感じです。あんまりちゃんと考えられてないので、おかしなところがあると思いますが……DBを青く塗っています。ここは私も学びたいので、ツッコミやコメントをお待ちしています。

f:id:yach:20210409105449p:plain

こちらの講演時にいただいた質問への回答です。

「テスト自動化とテスト駆動開発」講演資料 - やっとむでぽん

質問8. リモートでペアプロするときのメリット、デメリットはありますか?

リモートペアプロは、ツールやデバイスを上手く使えば十分に有効です。現在多くのチームがリモートペアプロ(リモートモブプロも)を実践しています。

f:id:yach:20210409135636p:plain

オンサイトのペアプログラミングは、人間がもっとも得意な、全チャンネルを用いたコミュニケーションを活用するものです。目の動き、ちょっとした表情、呼吸の変化、指先や姿勢の動き、仕草、声色、そうした表現をフルに双方向でやり取りすることで、お互いの様子もつかめるし問題もあぶり出せるし、共同で意気込む空気も生まれます。ドライバーがふと眉をひそめたのを見て、ナビゲータが「何か気になる?」と聞いたのをきっかけにして、隠れたバグが見つかったりします。リモートではチャンネルの内ごく一部しか使えず、自然に伝わる力に頼れなくなります。

そのためリモートペアプロでは意識的に、使えるチャンネルを最大限に使います。

  • カメラは常時ONで顔出し
  • マイクも常時ONで、つぶやきや息づかいも聞こえるよう高級なマイクを使う
  • ドライバーが自分の画面を共有する
  • 両者が大きなモニタを使用した上で画面全体を共有する(ウインドウだけではなくて)
  • 常時声を出すよう意識する

などです。頭の中を声に出したり、大げさに表情を作ったり、感情も含めて音声で表現したりしていくと効果が高まります。仕事の話だけだと感情が乗らないので、わざと雑談や無駄話を入れたりもします。

リモートでも身体性を生かせるよう「やったー!」と言おう

うまくいったらすぐ、2人一緒に「やったー!」と声に出して、両腕を上げてバンザイもする(身体も使って表現すると、人間の身体内部は驚くほど反応するので、ぜひやってみてください)。オンサイトだと拍手することも多いのですが、ZoomやTeamsなどのノイズキャンセリング機能で拍手の音が消えてしまいがちなので、声を出してやってください。

問題に関するコミュニケーション(仕事の話)もリモートでは不利になります。オンサイトでは画面を指さしながら「そこ間違い」「これをこっちにして」「それが先、次がそれ」など指示語を使いながら、同じものを見て、同じところに視線を当てて、認識を合わせていきます。リモートではこういう指さしが弱まります。ツールによっては人の画面共有に介入する機能もあり、擬似的な指さしができます(Zoom)。機能がない場合は、行番号を言ったり、テキストを読み上げるなどして伝えなければならなくなります。これも意識的にやらないといけないので、リモートペアプロのほうが疲れやすい感じが、個人的にはしています。

リモート特有のメリットもあります。スペースの問題がなく、手元のPC・環境のまま参加でき、キーボードやエディタの違いが問題なることもないので、オンサイトのときより気軽にペアプログラミングのセッションを開始できます。「ちょっといいですか」くらいの声かけでペアプロに入れるし、普段なら距離が遠くてペアが組みづらい人でもハードルがありません。パッと始めてさっと終われるので、短時間のスポットセッションもやりやすいです。ソースコードの共有だけは問題になりますが、gitなどリポジトリを経由する、クラウド上に共有の開発環境を準備する、などで対応できます。

リモートで今のところ代替できていないのが、ホワイトボードや大学ノートや裏紙のような、一緒に書き込みながら議論する媒体です。オンラインのホワイトボードは色々ありますが(Google Drive、JamBoard、Miro、MURAL)、雑にハコや丸を2人で描き込みながら設計やUIの議論検討をするようなツールは、まだないようです。(個人的には、ペンタブレットを使って、同時に描画できるようなツールが有用ではないかなと考えています。Windows であればMicrosoft Whiteboardはいいかもしれません)

リモートペアプロに限らず、リモートワークの懸念点ですが、ツール、デバイス、環境が些細な点も含め影響してきます。ネットがたまに不安定になる、声が聞こえづらい、背景ノイズがある、画面が小さい・狭い、ツールに制限がある、VPNが遅い、シンクライアント、PC内蔵マイク、カメラがない、などすべて、パフォーマンスに直結します。

ちょっと不便、ちょっと遅い、ちょっと聞こえにくいくらいだと、ついついそのままにしてしまいがちですが、それを1日中、何ヶ月も続けていると、フラストレーションも溜まるし、コミュニケーションそのものを減らす・避ける方向に働くおそれもあります。目にした範囲でも、以下のような例があります。

  • ネットが細いのでカメラOFFで(表情も大事なのに)
  • セキュリティ制限で使えない機能がある(該当機能だけでなく、いろいろ工夫するモチベーションを阻害する)
  • 外で工事してるので、話すときだけマイクONにします(発話する量が絶対に減る。ノイキャンマイク付ヘッドセットを支給すべき)
  • PCのスペックが低く、開発環境とコミュニケーションツールを同時に使うと仕事にならない(社内ルールで事務用PCとごっちゃにしている。自家用車と工事用特殊車両を同じルールで購買するような、現場を見ていない感)
  • セキュリティルールで、オンラインのサービスを制限・禁止する(ホワイトボードが使えなかったり、環境の共有ができなかったり、技術調査ができなかったりと、致命的)

こちらの講演時にいただいた質問への回答です。

「テスト自動化とテスト駆動開発」講演資料 - やっとむでぽん

質問7. TDD導入に関する質問です。 実際にテスト駆動の場合は、最初のリリース時は以前に比べると工数、費用はかかり、運用保守まで考えて効果がでてくるはず。最初のリリース時の費用はどうしても上がりそうです。 それを顧客に説明するときの考え方、作戦、政治、などが気になります。他社導入時の工夫など事例はありますか?

f:id:yach:20210407143822p:plain:w350

難しい質問ですね…ご質問ありがとうございます。前提とするプロダクトの状況によって、考え方が変わってくると思いました。1.初期開発なら、テスト駆動開発工数が増えることはありません。2.既存システムの再開発や、保守運用では、先行投資が必要になります。いずれにしても、3.一人前に作業できないなら、それは仕事でやって大丈夫?という話になります。もう少し詳しく説明します。

新規開発ならシンプル

新たなソフトウェアを新規開発する(1.)という幸運な状況では、最初からテスト駆動開発を導入するのがベストです。単にテストが自動化できるだけでなく、目の前の仕事をより品質よく実装していけるので、進みは速くなりますし、デバッグや再テストで取られる工数も減ります。スライドではこちらのデータを紹介しました(スライド81)。

f:id:yach:20210407143646p:plain

そもそも、テスト駆動開発を使いこなせる人であれば、それを「まったく使わない」という選択肢はありません。「ぜんぶテスト駆動で書く」「部分的にテスト駆動で書く」といった選択肢がありますし、実装する部分(フロントかバックエンドか、ビジネスロジックかボイラープレートか、フレームワークを利用するかどうか、など)でも最適なアプローチが違いますし、さらにレッド・グリーン・リファクタリングのサイクルを守る度合いも調節できます。ルール化するなら、「プロダクトコードにはテストコードを書く。タスク完了時(コミット、プルリクエスト、コードレビュー依頼など)には、必ず両方揃っていてテストはグリーンであること」というくらいがいいかもしれません。熟練者であれば状況によって選択肢を選べるので、必ずしも厳密にテストを先に書かなくても、サイクルを大きく1日1回くらい回すのでも、ちゃんとした結果を出せます(これは初級者は真似しないほうがよいです)。

和田卓人氏(タワーズエスト)の講演「質とスピード」では、コードに対しテストを書いてきれいな状態を維持することが、開発スピードの向上につながるという話が繰り返しされています。またMartin Fowlerの言葉として、「品質の低いコードは、数週間でスピードを大幅に遅くしてしまう」(“Developers find poor quality code significantly slows them down within a few weeks.”)と紹介しています。こちらのスライドは、開発スピードに関する重要な知見を含んでおり、読んでみるのをお勧めします。

f:id:yach:20210407143722p:plain

speakerdeck.com

再開発や保守運用は工夫が必要

さて、新規開発ではなく、既存システムや保守運用の場合(2.)は話が変わってきます。いまからテストを自動化するというかなり重大な先行投資が必要になるためです。この負担をまるごと一括で顧客に求めるのは、かなり難しいと思います。

ひとつのアプローチは、細く長くテスト自動化を内部で進めていき、徐々に内部の工数が減っていく効果をねらうというものです。細く長く工数を確保しないといけない話ではあるので、厳しい面もあるとは思います。いっぽう、現場のメンバーが何名かいて、TDDやテスト自動化をやってみたいというモチベーションがあって、仕事の中で時間を捻出して自主的に進めるというのは、よく見る姿です(工数を確保してあげられないのはブラック風味ではあります)。組織の共通予算のようなところから、戦略的にどこかのチームに投資するという選択肢も、あるかもしれません。予算(工数)確保であったり、スキルあるメンバーがやって来てパートタイムで手伝ってくれるなどです。

そもそも投資である

3番目の論点は、テスト駆動開発や自動化が初めてです、という人にお客様の仕事をさせるのでしょうか?という話です(3.)。これは開発サイドから見ると投資です。開発機材を最新化したり、未知の言語を勉強したり、新しいクラウドアーキテクチャを試してみるというのと、同じ意味で投資です。そうした投資の話をお客様とできる関係性なのであれば、ぜひ説明していただければと思います(コンサル的役割で説明のお手伝いもできるかもしれません)。

投資をお客様に求められる状況にないのであれば、初めてのテスト自動化をプロジェクトの予算でやるわけにはいかないでしょう。勉強、練習の工数は別途確保して、ちゃんと仕事で使えるようになってから仕事で使うという流れになります。スクラムを導入して、ちゃんと改善をまわせているチームであれば、改善をするという時間を確保しているはずなので、その中で勉強・練習をすることになります。

勉強と練習をするのはそんなに長い時間ではなくて大丈夫でしょう。勉強だけの時間が1週間(40時間)あったら素晴らしいですが、そこまでやらなくても、いったんできるようにはなると思います。そこからは仕事の中でやっていくことになりますが、書くテストの種類や、フレームワークの取り扱い、データベースの扱いなど、自分たちのプロダクトの環境で必要になるやり方を調べたり、編み出したりしていく必要もあります。最初の勉強から、プロダクトでテストを書くやり方があるていど安定するまでは、経験者や外部のメンターなどがいるほうが安心です。

勉強と練習をするのは、実際のところ自分たちはどのくらいのスピードで進めるのか、それを見極めるためにも必要です。納期とスコープが決まっていて、それに間に合わせないといけないのなら、実際の仕事の環境でスピードを計測するのはリスク対策としても必須です。やってみて、明らかに間に合わないなら、いまは諦めるという選択肢が採れます。テスト駆動開発でやると決定してから、それでは間に合わないとわかるのでは、手遅れになってしまうかもしれません。現実問題としては、練習してできるようになったうえで、その前提で見積もるはずです。

回答が長くなって恐縮ですが、懸念となる点を図で整理するとこうなるのかなと思いました。

f:id:yach:20210407143822p:plain

(字が読めなかったらごめんなさい。「現在の生産性とそれを基準とした見積り」「TDD勉強中~試行サクゴ中の生産性」「TDD上達後の生産性」です)

  • Bの生産性は本当にAより高くなるのか、どのくらいなるのか?
  • 勉強や試行錯誤で生産性が下がるCの期間はどのくらいかかるのか?
  • Cの期間中の仕事はどう見積もるのか?
  • Dの投資をどう捻出するのか?

こうした疑問には、申し訳ないですが私には回答できません。現状によるというところもあるし、結局は人次第の面もあります。Cを圧縮するためDを深くする、あるいは逆にCを引き延ばしてDを薄くするというような作戦もありそうに思えます。

私自身の、何年か前にアジャイルコーチとして支援したチームで、まず普通に作ったシステム(テストなし)に対し、エンハンス(保守)の中でテスト自動化とテスト駆動開発導入に取り組みました。なので既存システムのシナリオ、ただし初期メンバー(けっこう優秀)がそのまま追加開発をしている体制です。そこではかなり感覚的ですがBはAの1.2~1.3倍、Cは半年くらい、Dは主にメンバーのモチベーションで捻出した(ただし内製なので政治的な話にはならない。開発チームがビジネス側を説得した)、という感じです。

TDDではなくテスト自動化で考えると、プロジェクト全体で見て、手動でテストする方法と、テストを自動化する方法、どちらが工数面で有利かは、状況によると思います。すみませんが私には、そのへんのノウハウがありません。「自動化するだけで安くなる」はまやかしですが、手動と自動をうまく使い分ければトータルのコストを下げられる可能性はあるのではないか、少なくとも選択肢として俎上に載せられるのではないかとは思います。

こちらの講演時にいただいた質問への回答です。

「テスト自動化とテスト駆動開発」講演資料 - やっとむでぽん

質問6. テストコード部分の品質はどのように維持するのでしょうか?テストコードのテストコードを作るのではないと思いますが……

ひとつは、プロダクトコードと同様のレビューとリファクタリング。もうひとつは、テスト駆動開発に特有ですが、プロダクトコードとテストコードがお互いに支え合うような関係にする考え方です。

テストのレビュー

まずはレビューですが、これは普通のやり方です。テストコードの書き方、テストの内容、他のテストと見比べた上で全体を把握しやすく書けているかなどを見ます。特に、テストコードや実行結果から、テスト一覧を出力したり、失敗時の表示などを見て、どんなテストをしているか把握できると、テストドキュメントをある程度代替できるので、そうした観点で読めるか・伝わるかも見ます。

プロダクトコードのレビュー時に、TDDで書いたテストコードも一緒にレビューするのも一般的です。急いでいるときはテストだけレビューすれば、何が動いているか、どこまで進んでいるかわかるというメリットもあります。

極端に品質の低いテストコードには、以下のようなものもあります。

  • テストの実体がない(ガワだけ。書き忘れ、時間不足)
  • テストが実行されていない(タイプミスなどで、けっこうやりがち)
  • なにも検証していない(assertがない。テストを理解できていない。コードカバレッジを増やすためにやってる場合もあるが、無意味)
  • テスト対象のプロダクトコードが、なぜかテストコードにコピーしてある(テストへの理解不足。「このコードをテストしたいんだ」という気持ちはわからなくもない。コピペ開発のやりすぎかも)
  • 書いた人の環境、PCでないと実行できないテストになっている(ファイルアクセスが必要、特殊なテストデータが必要などで、全員が実行できるようなテスト環境を整えられていない場合に見られる。開発ができる人ほど、できちゃうので、やりがち)

メンバーの理解不足、スキル不足が原因の場合もありますが、時間がなくてつい…ということもあります。人の目によるレビューは、だいたいいつも有用です。ペアプログラミングをしていれば、レビューは減らしても大丈夫かもしれません。ペアプロでも、コードレビューをまったくゼロにするチームは少ないようです。

テストコードもリファクタリング

レビューからのフィードバックを取り込むのは、テストコードのリファクタリングです。もっとも、レビューがなくても、リファクタリングではいつでもテストコードを対象にします。ここでは「コードとしてのリファクタリング」と「テストとしてのリファクタリング」を両方とも意識する必要があります。

テストコードもコードなので、プロダクトコードと同様、読みやすく、重複なく、変更しやすいようにリファクタリングをします。基本的にはプロダクトコードのリファクタリングと同様です。ただしテストコードの場合は、「どんなテストケースなのか」を読み取れるのが最優先となります。プロダクトコードは、コードを書く人しか通常読みませんが、テストコードを読む人はテストの内容を知るために読む場合が多くなります。そうするとロジック(How)を把握するより、何をしているのか(What)を把握するのが早いほうが、嬉しい。

私は、読む人は以下の順にテストコードを把握したいだろうと想定して、できるだけ早く必要な情報がわかるように意識しています。

  1. どんなテストか(What) - テスト名(メソッド名など)を見ればわかるように
  2. 確認している内容は何か - assert部分を1行だけ見ればわかるように
  3. どうやってテストしているか(How) - テストメソッド全体(だけ)、特にassertまでの準備部分を見ればわかるように
  4. テストを書き足すにはどうするのか - 共通化されたセットアップ、フィクスチャやヘルパーを含めたテストコード全体を見る

また、少しずつ異なる複数テスト内容が、少しずつ異なる複数のテストメソッドで実装されます。この「少しずつ」の差異は、テストコードの中に埋もれてしまって読み取りづらくなりがちです。「あれ、このテストとこっちのテスト何が違うんだろう…(5分後)あー、ここが123546789か123465789かの違いかー」みたいになります。この間違い探しはツラいし時間を食いますし、さらに違いを見落とす場合もあります(実はさらに"あああああああああ"と"ああああああああああ"の違いがあった、ということもある。なお、こんなテストを書いた人は呪われてしまえと思う)。

こういう差分が素早く確実に読み取れるような工夫も、テストコードには大事です(特有なわけではなく、プロダクトコードでも役に立ちますけれど)。定数を使う、インデントを揃える、コメントを入れる、名前付きパラメータやデフォルト値を活用する、フィクスチャなどで再利用する、前準備をテストのネストで構造化する、ヘルパー関数を作る、カスタムアサーションを作るなどのテクニックがあります。

テストとしてのリファクタリングも、ここで考えるべきです。テスト駆動開発で書くテストは、開発を進める順番に書いていくので、後から見ると「なにをテストしてるのか」「なんでテストしてるのか」わからなくなりがちです。ひとまとまりの機能が完成して、その機能をテストがだいたいカバーしているタイミングで、今までに書いたテストを見渡します。一貫性があるか? 仕様、なにをする機能か(What)がテストから読み取れるか? テストどうしの見分けがつくか? 全体としてどんなテストをしているか把握できるか? 他の機能のテストと並べてみて、意味があるか? テストの重複はないか? 足しておきたいテストは?

作業が一区切りついたとすると、次にこの部分をさわるのは、もしかしたら半年後かもしれません。この部分をまったく知らない別の人(ペア)が、また新しい仕様変更に対応するかもしれません。そのとき、テストが作りっぱなし、書きっぱなし、半年前に作業したペアにしかわからない状態だと、ひどく苦労することになります(そして元々のペアも半年前のことはもちろん覚えていません)。「テストが整理されていない」状態では、テストを適切に変えることもできなければ、テストを命綱として安全に変更することもできなくなります。

テストコードを他の人が読むことを想定し、またテストの内容や網羅性、観点やテスト全体での位置づけを伝えるのが大事だという認識で、テストコードをリファクタリングします。テスト名を仕様の言葉で表現し、正常系なのか異常系なのか、境界値をテストしてるのか、組み合わせをテストしているのか、見分けがつくようにします。テストをネストして分類したり、同種のテストをグルーピングしたりします。パラメタ化テストも便利です。こうしてリファクタリングされたテストは、QAテストでも役に立ちます。

テストのリファクタリングでもっとも難易度が高いのは、テストの削除です。すでにあるテストが、重複しているように見えるのだけど本当に消していいのかわからない…ここで消してしまう勇気はなかなか出ません。実際問題として、自信を持ってテストを消せるのは、テストを書いた当人だけ、テスト書いた直後だけです。書いたばかりのテストをリファクタリングするタイミングであれば、これはこういう事情で書いたけど、もういらないという判断ができます。不要なコードを後に残すのは、メンテナス性を下げるだけです。不要なテストの削除も含めて、テストのリファクタリングを忘れないでください。

コードとテストが支え合う

もうひとつ、テスト駆動開発ではプロダクトコードとテストコードを使ってお互いに動作確認をしあうことがあります。テスト駆動開発の技法として仮実装(Fake It)というものがあります。これは、テストコードを書いた段階で、確実にテストを成功させる(Greenにする)ため、プロダクトコードに偽物(Fake)の簡易な実装をします。実際の計算をする代わりに固定の値を返すなどです。もしもこの時点でテストが成功しなかったら、問題があるのは(固定値を返すだけの)プロダクトコードではなく、テストコードです。こうして、テストコードが正しく動作するかを確認します。テストのための初期化が足りていなかったり、呼び出す対象が間違っていたり、結果の確認方法が違うなどの問題を検出できます。

同じアプローチは、書き始め以外でも使います。コードのこの部分を変えると、このテストがこういうふうに失敗するはずだと考えて、実際に変えてテストを実行します。ここでテストが失敗しなかったり、失敗しても想定と違うメッセージを出していたりすると、テストが間違っている(あるいは自分の理解が違う)とわかります。

TDDのレッド・グリーン・リファクタリングのサイクルは、数分くらいとごく短いですが、このアプローチを使っているときは

  1. 試しに1文字変えて実行→
  2. 期待通りRed→
  3. 正しいつもりで数文字直して実行→
  4. 期待通りRed→
  5. さらに数文字直して実行→
  6. 期待と違うRed→
  7. いったんコードを戻してGreen→
  8. テストを直してRed→
  9. 正しく直してGreen

というように、1分以内、秒単位のサイクルで、コードとテストの挙動を細かく確認しながら進める場合もあります。こうして、テストが機能していなかったり、想定と違うテストをしてしまっているような状況を防ぎます。

こちらの講演時にいただいた質問への回答です。

「テスト自動化とテスト駆動開発」講演資料 - やっとむでぽん

質問5. 実際にTDDで運用したとき、それでも不具合が発生したことがありました。 データの量、質が問題視されたことがありました。 受け取った情報の信憑性は会話しながら模索しないといけないのでしょうか

かつては、TDDしていればテストはそれで十分、それ以上のテストは不要と言われていたことがありました。現在は、TDDだけで品質を確保するのは難しく、それ以上のテストも必要だと言われています。

テスト駆動開発は、開発手法です。開発を駆動するために、プログラマーがフィードバックを得る道具としてテストを書きます。このテストは、開発を進めるには十分ですが、品質を担保するには不十分です。とりわけ、仕様そのものが曖昧だったり、抜け漏れがあるような場合、テスト駆動開発は無力です。

かつて「TDDで十分」と言われていたのは、これは個人的意見ですが、当時実践していたプログラマーがテスト設計もできるようなスーパーマンの人たちで、TDDのサイクルを利用していろいろな観点からテストを十分にできていたからかもしれません。いま挙げた、仕様に問題があるような場合でも、TDDをしながら「この仕様おかしいな?」と気づいて、お客様に問い合わせできていたかもしれません。(TDDには仕様の不備を見つけやすい効果があると、個人的には感じていますが、誰でもそれだけで十分というわけにはいきません。)

そういうわけで、ある意味至極当然ですが、TDDによる開発とは別にテストをすることになります。この「テストをする」は、手動テストと、テストを追加で自動化する両方を含みます。仕様書をレビューしてテスト設計をする活動が開発と並行して進めたり、開発できたものを触りながらテストしたり、開発完了前に自動化したテストを準備したり、チームやプロジェクトのテスト計画をもとした活動になります。

どんなテストが足りないか

まずTDDで書くのはユニットテストコンポーネントテストに当たるのがほとんどです。それ以外のテスト、統合テストやシステムテストなどは、他のアプローチが必要です。またユニットテストでも、TDDだけではテスト観点が足りていないので、あらためて追加のユニットテストをする必要があります。TDDしているなら、追加も自動化するのが自然でしょう。

TDD以外でテストを自動化することを、対比してQAテストと呼ぶこともあります(もっと広い活動を指してQAテストと呼ぶほうが一般的だと思います。ここでは説明のため、TDD後のテスト自動化をQAテストと呼びます)。TDDでは開発を駆動するためのテストを書きましたが、QAテストでは品質を保証するためのテストを書きます。この過程ではTDDで書いたテストも利用されますし、また整理や再構成(リファクタリング)の対象にもなります。QA観点ではテストが足りない場合もあれば、過剰なこともあり、またテストの位置づけを変えたほうが理解しやすかったりもします。

こちらテストを書くか書かないかの判断の話 · GitHubではこのように述べられています。この方は「TDDのサイクルを利用していろいろな観点からテストを十分にでき」そうに思えます。

ある程度実装が固まり、これでいこうと内心思えたタイミングがテストケースの網羅率を上げるタイミングです。ここでは TDD のテストではなく、QA (品質保証) としてのテストの観点に自分の頭を切り替えます。ここで初めて、同値分割や境界値分析による単機能のテスト設計をします (とくに無効同値クラスに分類される異常系の処理のためのテストを充実させることが多いです)。

こちらTDDのテスト vs. QAのための単体テスト について考えてみた - What is it, naokirin?では、TDDで書くテストと、QAのための単体テストの違いについて考察されています。

TDDのテスト 単体テスト
コーディングのため 品質(保証)のため
開発促進のため 欠陥を取り除くため
リファクタリングのため 品質確認のため
テストファーストで行う コーディング後に行う
詳細設計によるテスト 単体テスト設計によるテスト
設計技法としてのテスト 検証と妥当性確認のためのテスト
開発者が即座にフィードバックを得るため
プログラマのためのテスト
三角測量

こちらJaSST'12 TokyoのTDDセッションとWACATEセッションに登壇 - 千里霧中では、goyokiさんが2012年のJaSST(ソフトウェアテストシンポジウム)でのVOTDD(検証指向TDD)で、TDDのサイクルに検証指向(品質保証のひとつの観点)を含めるやり方について、発表とデモをされています。発表スライドが表示されない場合があるようですが、ページ内のリンクからアクセスできます。

資料の最後の方に「VOTDDの定義」の解説ページを追加しています。詳細はそこを参照して頂くとして、非常に簡略化して言うと、TDDでもテストについてもっと気をつければ色々良いことあるよというのがVOTDDです。

TDDでどのくらいテストを書くか、QAテストをどのくらい追加したりリファクタリングしたりするかは、プロダクトや組織によっていろいろです。気になるところをちょっと追加する程度から、何倍ものテストを書き足すところもあります。これは品質保証の戦略と、その中でのテスト自動化の位置づけに関わってくる、大きなテーマの一部でもあり、私が紹介できる範囲を超えています。

自分ならどうするか(TDDと直接関係ない補足)

元の質問に戻って、データというのはシステムへの入力データで、テストで使ったデータが不十分だったり、検討不足だったという状況かなと想像しました。個人的な経験も踏まえて考えてみます。お客様から「これが入力データだから」とデータフォーマットやテストデータを受け取った場合は、

  • 本物(本番データ)と、必ずどこか違う
  • 「0か1だけ」とされているフィールドに2が入るようなことが、最低1箇所はある
  • 本番で想定する件数(データ量)を扱うと、なにか問題が起きる
  • あり得ない矛盾したデータが本番では来る(存在しないデータの更新差分とか、明細が0件のオーダーとか)
  • 「これが最終版です」というバージョンが来てから、また新しいバージョンが届く

こういうことがあると想定します。想定した上で、開発と並行してどんなテストケースが必要か考えつつ、自分たちで使うテストデータを作ります。ここで作ったテストデータは、場合によっては、テスト駆動開発のサイクルで使うデータにすることもあります。あとで手動で流す用にすることもあります。本番環境の実データと比較して、どこが想定と違ったか調べる材料にも使います。

ソフトウェアの性質にもよりますが、別システムから受け取るデータは「得体の知れないよそもの」と思って、実行時のバリデーション(入力チェック、フォーマットチェック、整合性チェック)もかけます(これはfail fastのアプローチでもあります)。お客様のことは信頼しても、お客様が運用しているシステムは信用しないというわけです。バリデーション処理の開発は、いろいろ想像力を膨らませながら異常なパターンを考えて設計します。TDDのサイクルで、新たな可能性に気づいてパターンを補充したりもします。

現実として不具合が発生してしまったら、それ自体は仕方がないので対応します。可能な限り、不具合を再現するテスト(データも含む)を必ず書きます(TDDもそうでなくても)。テストを書いた上で修正をするという順番です。 もうひとつ、不具合がどうして発生してしまったのか分析するのも大事です。テスト漏れだとなったら、既存のテストも見直して漏れがあったら追加して自動化します。ここで新たに(未発生の)エラーを見つけられるかもしれません。コミュニケーションの改善が必要であれば、そうした施策を打ちます。QAチームがいたら分析と対策の活動を主導してくれるはずです。