chibaqn /
Posts
2022-08-04 00:00:01

リーダブルなテストコードについて

はじめに

過度なDRYは読みやすさの敵!?「リーダブルテストコード」という発表をしました #vstat


以前よりテストコードの実装について課題感があった。

上記のスライドを見て、自分の考えを整理したい(スライドの内容が自分の考えと近そうなので、考えを進めるのに参考になりそう)と思いこの記事を書いた。

もともとあった課題感

Rspec には DRY を実現するための機能がある(shared context, shared example, subject など)。 しかし、それらを活用した場合テストコードの可読性が下がっていると感じる(読むのが辛い)。

テストコードは共通化よりも「多少冗長でも、スコープを短くすること」を優先した方がよいのでは🤔

理想と現実のギャップ

テストコードの理想と現実とのギャップを表にまとめた。

テストコードはドキュメントとしても理解しやすいことが重要であり、不適切な共通化(過度な DRY) がギャップの原因となっていることを再確認した。

理想 現実 原因
上から下に素直に読める 上下をいったりきたりする スコープが広い。
let
shared context
shared example
subject
仕様がひと目でわかる 「何をテストしているのか」を読み解かないといけない ・過度な DRY(不当な抽象化)。
・AAA パターンを意識できていない。
・複雑なテストロジック。
確実にバグを検出できる プロダクションコードの振る舞いが変わったときにテストコードの振る舞いも変わってしまい、バグを検出できない 下記の定数の話が原因の1つかな?

定数の話

アプリケーション側の仕様変更に合わせてテストコードが仲良く二人三脚で歩いて行くようなコードを書くと、アプリケーション側の実装にバグが埋め込まれた場合にもテストがパスしてしまう可能性があります。つまり、バグを検知すべきテストコードがやすやすとバグを見逃す可能性があるわけです。これは致命的な問題です!

Q. アプリケーション側に定数があった場合、その定数をテストコード側でそのまま利用しても良いか?

リーダブルなテストコードを実装するための方針

共通化よりも可読性(上から下に素直に読めること)

テストコードはドキュメントとしても理解しやすい状態を目指す。

  • 上から下に素直に読める状態にする。
  • shared context, shared example, subject を使用する場合、可読性を犠牲にしていないか注意。可読性を犠牲にしている場合、使うのを辞める。
  • スコープが広い let を定義するときは、可読性を犠牲にしていないか注意。可読性を犠牲にしている場合、使うのを辞める。

AAA(Arrange-Act-Assert)パターン

Arrange-Act-Assert パターンに沿って実装することも可読性を高めるためには重要と思う。

Arrange, Act, Assert それぞれについて、 Rspec でどこに実装するべきかを表にまとめた。

ステップ 実装場所
Arrange(準備) before, let/let!
Act(実行) it/example
Assert(検証) expect

実装例

describe '#call!' do
  # Arrange
  let!(:user) { create(:user) }

  context '~の場合' do
    # Arrange
    before do
      ...
    end
    it '~する' do
      # Act
      user.call!

      # Assert
      expect(user.nickname).to eq('chibaqn')
    end
  end
end

余談(DRY の誤認)

DRY 原則について「コードの重複をなくすべき」という理解は表面的なものだと思う。

正確には(本質的には)、「ソフトウェア内で目的が同じ情報を重複させない」という原則だと考えている。つまり、目的が異なるロジックであれば、同じような(似ている)ロジックでも DRY にすべきではない。

おわりに

伊藤さんのスライドがきっかけで、何となくモヤモヤしてたことを整理することができた。

素敵なアウトプットをいつもありがとうございます。