はじめに
Ruby on Railsのアプリケーション開発で、上司とのペアプロでTDD(テスト駆動開発)を学ぶ機会があったので、そこで感じたTDDに対する自分の認識の変化と学びをまとめます。
(僕個人がとらえた内容になるので、間違いや語弊があれば教えてください)
テストについての認識
まず、それまでほとんどテストを書いたことがなかった僕は、テストについて以下のようなイメージを持っていました。
- プログラム以外にもテストコードを書かなければならないから開発工数がかかる
- プログラムの挙動を全て担保するテストを書くのはかなり大変そう
しかし上司によると、まずテストに対する認識を変えると楽に考えることができて、気軽にテストが書けるようになるとのことでした。
それはどういうことかというと、
- テストに対して過大な期待はしない
- テストで全てのバグをなくせると思わなくて良い
- テストの役割はプログラムを動かすためにチェックすること
- 自分が書いたコードの動作を担保するために書く
と認識すると良いということでした。
それまで、テストの役割はプログラムの全てのバグを潰すことだと思っていた自分にとってはとても新鮮な考え方で、確かに上記のように捉えるとテストが気軽なものに感じました。
TDDを体験する
上記の内容を体系的に学ぶために、あるバグフィックスに対して上司と一緒にTDDを行いました。
0. バグの内容を確認する
稼働中のプログラムで、以下のような予期しないエラーが発生していました。
NoMethodError
undefined method `downcase' for nil:NilClass
エラー箇所のコードは以下のようになっていました。
email_from_id_token = id_token.email.downcase
Apple IDでサインインをする処理で、ID情報を参照している箇所でした。
id_token
の中には必ず email
が存在する前提で書いていますが、email
が存在しないことがあるようで、そのことが原因のエラーのようでした。
1. まずRSpecでバグを再現させる
RSpecで上記と同様のエラーが起きるテストケースを書いて再現させました。
context 'when email not available' do
#let(:payload) { { iss: AppleID::ISSUER, sub: external_guid, aud: 'hoge', exp: 0, iat: 0, email: email } }
let(:payload) { { iss: AppleID::ISSUER, sub: external_guid, aud: 'hoge', exp: 0, iat: 0 } }
it_behaves_like 'return bad request', nil, I18n.t('api.errors.email_not_available')
end
when email not available
(emailが取得できないとき)というテストケースを作成しました。
モックとして、apple_idというgemを使ってemail
を含まないトークンを作成し、その場合に期待する結果を記載しました。
ここで一度テストを実行し、「0. バグの内容を確認する」で発生していたエラーと同様のエラーが発生して、テストが「失敗」することを確認します。
2. プログラムを修正する
次にテストが成功するようにプログラムを修正しました。
email_from_id_token = id_token.email&.downcase
if email_from_id_token.blank?
return render_bad_request(display_message: I18n.t('api.errors.email_not_available'))
end
id_token.email
が取得できないパターンを考慮して適切にエラーハンドリングの処理を記載しました。
3. 再度テストを実行する
この状態で先ほどのテストを実行すると「成功」になり、予期しないエラーを適切にエラーハンドリングできてバグが解消されたことを確認できました。
これにて対応完了です。
まとめ
テストを書かずにプログラムを直した場合、エラーが解消したことを確認するためにエラーが発生する状態を作って検証する必要がある(今回の場合、エラーが発生するのはレアケースだったので、実際に実機で再現させるのには手間がかかる)
また、動作確認ができるまで修正方法が正しいことの確証が持てません。
しかし、TDDではモックを使ってエラーが再現する状態を先に作るので、エラー解消の確認が容易にできますし、自分の書いたコードによって正しくエラーが解消されたことの確証を持つことができます。
今回、TDDの素晴らしさと楽しさを学ぶことができました。
今後はあらゆる場面で活用していきたいです。