皆様、Spec(コードベースの単体、機能、結合テスト)書いてますか?
私はあんまり書いていません。
「テスト書かないとかありえない!」と怒られそうなので一応弁解しておきますが、C0レベルのテストは、そこそこ書いているつもりです。
プロジェクトでカバレッジはXX%以上!(XXの部分は95だったり100!だったりします)という決まりがあるため、書いてます。消極的な理由ですね。
時としてカバレッジを満たすためのテストを書く、という意味の分からない事をしてしまったことは、反省しております。
しかし、C0は所詮C0であり、このレベルのテストをいくら一生懸命書こうが、バグがでるときはでるものなのです。
だからといってC1、C2、またはそれ以上を満たすようなテストを一生懸命かけばバグが無くなるか?という話でもありませんが、バグ発生率が少しは下がるかと思います。
さて、最近お固い業務システムで、そこそこ致命的なバグをやらかしてしまいました。
今後もC0レベルのSpecを書いてて良いのか? という話題がプロジェクト内から湧き上がったので、いろいろ考えてみることとしました。
なぜ事前にバグを発見できなかったのか。私にもわかりません。
バグを生み出してしまった時は、必ず再発防止策を考えねばいけないのですが、
「今後同じようなバグを発生させないために、どうしたら良い?」と聞かれても、なかなか答えることができません。
単にテストを怠っただけなのでは。手動テストをもっとしっかりしろ、またはテストコードをもっとちゃんと書け、と言われると、反論できません。
「仰るとおりです」としか言えない。
バグのないシステムは無い!と開き直りたいのですが、そうもいかず。
「期間がなかったから、予算がなかったから、、、」という言い訳も思いつきますが、これで相手を納得させるテクニックは、今の私は持ち合わせておりません。
物凄く単純なバグは、発生することが少ない、と思ってます。
画面みてわかるレベルのやつですね。ボタン押したらInternal Server Errorとか。
C0レベルのテストは、ちゃんと書いてますからね。
めんどくさいのは、複数の条件が組み合わさって、初めて発生するバグです。
しかも目に見えないバグが一番厄介です。
手作業で行う、結合レベルのテストでも発見できない事が多いからです。
目に見えないバグとは何か?
ある条件の時には、DBのこういうテーブルにはAというデータが入って、さらに別のテーブルにはBというデータが。。。みたいなやつで、それらの情報はWeb上の画面には表示されません。
こういうのは、結合レベルのテストでも見逃されがちです。
目に見えない情報なんだから、別に重要じゃ無いんじゃない?と思われがちなのですが、本当にそうでしょうか。
業務システムの場合、目に見えない重要な情報を蓄えている事が多いと感じます。
あとで使うかもしれないから、とりあえずとっておくという類の情報です。
代表的な例は、監査証跡系ですね。
どの業者の、誰が、いつ、どんな操作を、どのデータに対し、どういう条件下で行ったか。みたいなやつのことです。
ログとか別にどうでもいいじゃん。 って思いますか?
業務システムでのログは超重要です。
私の関わっているシステムでは、ログに加え、データに変更が発生するタイミングで、変更前のデータのスナップショットも保存していたりします。
(1テーブルの関連テーブルが何十個もあって、変更が入るタイミングで関連データ全部のスナップショット取らないといけなくて死にそう。。。という愚痴も言ってみる)
まとめると、発見が厄介なバグは
のバグです。
システムのバグを0にする、というのは「無理」ですが、バグを少なくするための方法も、検討せればなりません。
「致命的なバグがでちゃいましたー。すみません。次から気をつけます。」というわけにもいかず、再発防止策を考えねばいけないためです。
予算とか期間とか無視していいのなら、以下の対策を思いつきました。
手作業で行う結合レベルのテストで、DBのデータを確認しろ!というのは、なかなかきついものがあります。
また、仕様変更が入ったタイミングで、あの大変な手作業結合テストをもう一度実施!となると、コストもかかるしモチベーションも下がります。
手作業でのテストはやめて、プログラムで自動化してテストしましょうよ!という流れになるのは、まあ当然といっては当然です。
目に見えないデータが、とある条件下で正しく設定されているか。を確認するためにはどうしたらよいか。
いろんな条件で、その機能実行後にデータが設定されているかを確認する、大量のテストコードを書けばよいのでは、
と思い、試しに機能レベルのテストを大量に書いてみました。
# とある業務システムの、機能Aに関するテストコード (Rspec)
describe '機能Aについて' do
context '業者AとBが提携関係にあるとき' do
before do
# データ準備
グループAを用意
グループBを用意
業者Aを用意
業者Bを用意
業者AをグループAに加入
業者BをグループBに加入
グループAとBで提携関係を結ぶ
業者AとBの提携関係を結ぶ
end
context '業者Aに加入しているユーザAが操作を行う'
before do
ユーザAをグループAに加入
ユーザAを業者Aに加入
end
context 'ほにゃららデータがあるとき' do
context 'さらにほにゃららが' do
context 'さらに...' do
it '機能A実行、モデルAのXXXにYYYが入ること' do
end
it '機能A実行、モデルAのZZZにAAAが入ること' do
end
... 以下、XXXのデータがYYYみたいに設定されていること、というテストが続く
end
end
end
end
end
end
テストを書いていて、気が付きました。
モデル(またはテーブル)AのXXXにYYYというデータが入ること、なんていう細かいレベルのテストを、色んなパターンで実施するテストを書くのは、めちゃくちゃ大変です。
というか、Rspecでこんな細かいレベルのテストコード書いてるの、見たことないぞ。
正直このレベルでテストを書く必要があるのか、甚だ疑問です。
それにテストコード書くのが、めちゃくちゃ苦痛です。
実装書いてる時間より、テスト書いてる時間の方が長い。
テストコードは書きすぎて困ることはない!という主張も、たまに目にします。
本当にそうでしょうか。
テストを書く上で一番大変なのは、テストデータを用意することです。
例えば、とある操作(例えば、物流システムを作っているとして、倉庫から別の倉庫へ商品を移動する操作)を実施するためには、
みたいな、複雑な条件があって、初めて操作が実施できるわけです。
どれか一つがだめだったら、操作できないとか、条件付きで操作ができるとか。
こういう条件を全部満たすために、色んなデータ・セットを用意するのは、相当大変です。
いままではFactoryGirl(というテストデータを作り出すためのライブラリ)で、ちまちまデータを作っていたのですが、これがいろんな条件下のデータを作りだしていると、テスト実行が遅くて遅くて。。。
テストパターンが増えれば増えるほど、テストが目に見えて遅くなっていくのです。
前提となるデータを都度生成しているためですね。
こうなると開発者各々でテストを実行させるのも、億劫になってきます。
仕様変更の際にテストが邪魔になるケースもあります。
最近、プロジェクトで大きな仕様変更がありました。
今私は、1機能を修正するたびに、真っ赤になった何百ものテストケースをちまちま直す作業をしてます。
カバレッジを満たすための、ゴミみたいなテストケースは消してます。でもカバレッジが下がる。。。
なにより、テスト書いてると開発が進まない。
テストが重要なのはわかりますが、実装している時間よりテスト書いてる時間の方が長いっていうのは、なんだかなという思いがあります。
バグがでちゃいけないシステムなんだから、テストもしっかり書くのは当たり前!と言われればそうかもしれませんが、やっぱり納得いかない。
まとめると、テストを書きすぎると、以下の問題が発生します。
つまり、開発スピードが落ちます。
業務システムも、開発スピードを求められる時代。
プロジェクトに1年も2年もかけてはいられないのです。
自動テストを大量に書くのは、開発スピードが落ちてやばそう、という気がします。
では、やはり手動でのテストしか無いのか?
仕様変更がほぼ発生しない状況下を創りだして、手動のテストを厚くするのが良いのか?
けど仕様変更が発生しないシステムなんて、今時ないですよね?(元々無い気もします)
それに、今回問題提起したバグっていうのは、Web画面上に表示されないデータのバグを、どうやって発見するか?ということでした。
手作業での結合テストで、DBに正しい値が入っている、なんていう確認をするテスト、やりたいですか?僕はやりたくありません。
テストコードをどの程度書けばよいのか?
その答えは未だでてはおりません。
結局のところ、手動テストと自動テスト、どっちを取るか?という話ではなくて、両方をうまい具合に混ぜてテストしていくしかないと思っています。
なんでもかんでも自動化すればうまくいくっていうことはありません。
ここはバグ出そうだな、という部分は自動テストも厚くするし、手動テストでももちろん実施する。
手動でのテストでも、場合によってはDBレベルの確認を含める。
あまり細かすぎる自動テストは、のちのち自分の首を締めそうなので、辞めたいところです。
今はその、丁度良い塩梅を見極めている最中です。
最近のシステム開発は、予算も無い、期限もない、でも高い品質を求められるという、もはやどうしたら良いのかわからない状況になりつつあります。
低価格、短納期、高品質、これら3つをすべて満たすことはできないのです。
でも、これらを満たすための努力はしなければなりません。
徹夜で仕事して達成する、とかそういう辛い話ではなくて、テスト自動化を駆使して、なんとか解決するための方法を考えなければいけません。
で、結局のところ、テストコードはどの程度、どのレベルまで書いたほうがいいんでしょうか。
皆様の意見を伺いたいところです。