テストコードで再現テストをしてから、bugfixするやつ。
これうまく行ったら超気持ちいいけど、いつも気持ちいいとは限らないのでメモ。
バグのパターンがユニットテストとしてエッジすぎ
だいたい再現テストコードがエッジなパターンすぎて、
仕様を説明する助けとして不適切になってしまう。
describe 'somemethod' do
subject { describe_class.new(params).somemethod }
context 'A' do
let(:params) { 'a' }
it { is_expected.to eq ('A') }
end
context 'B' do
let(:params) { 'b' }
it { is_expected.to eq ('B') }
end
# 再現テストのコンテキストがハイコンテキスト
context 'B and hogehogehoe' do
let(:params) { 'a' }
before do
# ハイコンテキストな記述
end
it { is_expected.to eq ('A') }
end
end
↑この程度なら全然いいんだけど野生のコードはこんなのじゃすまない。
このままマスターに入れてマージすると、仕様を説明することを阻害するよなぁ的に。
しかし、マージしておかないと同じバグを繰り返すかもしれないので、対策としてそのハイコンテキストなテストを別ファイルに切り出したりしてる。
コンテキストやフィクチャでテストのファイル分ける
1つクラスに1つのテストファイルがデフォルトですが、
テストファイルがって大きくなってきたら、コンテキストやフィクスチャ、メソッド単位でテストファイルを分割しても問題ないわけです。
なので、コンテキストが揃わないケースでは、別ファイルにコンテキストを分けてしまう。
そうすれば、仕様のキャッチアップにはよりプリミティブな浅いコンテキストのテストケースを読めるようにしておく。
そして再現テストは別ファイルで自動テストされるようにしておく。
というわけです。
なんかうまくいかない時に選ぶ他の手段
その他にもこのケースには下記の対応方法があるけど、場合によってはこれらも選んじゃう。
- 再現テストをマージしない
- 再現テストに価値がないと判断したときはTDDの道具としてだけ利用させてもらい、自動テストコードとしての役目は与えない。
- 新しくできたハイコンテキストなテストに粒度を揃えてテストを書き足す
- ぶっちゃけ時間かかるし、やらないこと多い。
- 別ファイルに切りださずにハイコンテキストなテストも既存のテストの間に入れる
- ファイル切り出すコスト高すぎる時とかありますよね。。
- 先にテストのリファクタが必要
- ハイコンテキストなテストを一度マージして記録に残しつつ、リファクタリングをしてハイコンテキストなテストを書かなくても良い仕様にした後にリバーとする
- 毎回これができれば最高。
- でもできない時がある気がする。時間とかコストの問題じゃなくて、スキルの問題かもしれない。
まとめ
基本的にバグに対して再現テストを書いて立ち向かって見てるけど、、、、
書きあがったテストコードがぐちゃぐちゃしちゃうのが悩み。
ぐちゃぐちゃでもいいから、別ファイルにテストコードを分けてコミットして再現テスト -> 修正すると良さそう。
そのやり方も万能じゃない。
そもそも再現テスト書いて、不具合に立ち向かうのをサボりたくなるときもある。