Rspecの書き方についてディスカッションしました(nikotama.rb #11)

こんにちは、ラクマでサーバーサイドエンジニアをやっている岸です。

突然ですが、楽天ではひっそりとRubyコミュニティを開催しています。正しくは、楽天所有のコミュニティというよりは、社内で業務内/業務外限らずRubyを触っている人たちが集まって、社外の方もご参加いただけるイベントを開催しています。

新型コロナウイルスの影響もありしばらく開催を控えていましたが、第10回目よりオンラインで再開し、今回が第11回目となりました🎉

今後も月1回ペースで開催していきますので、ぜひともイベントに登録してみてくださいね🥺

nikotamarb.connpass.com

Rspecの書き方

そこで、今回私が発表させていただいたのは「Rspecの書き方」についてです。

「実践Rspec」というと偉そうなタイトルですが、「(私が)実践(している)Rspec(の書き方、どうですか?)」くらいに読み替えていただければ幸いです(笑)

というのも、Rspecの機能自体は解説記事が溢れているのですが、どういう方針でRspecを書くか?という方針についてあまりディスカッションを見かけたことがなかったので、せっかくだからオレオレスタイルを晒して生の意見を頂こう!と思ったのがきっかけでした。

課題の定義

まず、自分が感じている課題としては・・・

アプリケーションが取りうる振る舞いの数=ロジックの集合体としての分岐数は、我々がテストを書ける限界数を簡単に超えてしまうことだと思っています。

解決方針

解決方針としては・・・

できるだけ疎結合なテストを行えば、少ないテスト量で品質が担保できるであろうことを述べました。

架空の実装としてはこんな感じで、外部へのHTTP通信やデータベースとのやり取りを含むロジックを、先程の解決方針に従って自分なりに実装してみた例を説明していきました。

詳細はスライドをご覧いただければと思いますが、基本的な letsubject の使い方から、 できるだけ依存性を排除するための double allow などのメソッドの使い方についても触れていきました。

自分の好みとして

allow(...).to receive(...).and_return(...)

よりも

allow(...).to receive(...) {...}

のほうが好きだったり、

let(:double1) { double('name') }
before { allow(double1).to receive(:hoge) { 1 } }

よりも

let(:double1) { double('name', hoge: 1) }

のほうが好きなこと、partial doubleの and_call_original の使いみちなども紹介しました。

partial doubleについてはこちら。

議論が沸き起こる

発表後、すぐに議論が始まり、主に以下2点についてディスカッションが行われました。

議論①: テストをDRYにすべきかどうか?

私はコードは短く少ないほうが好きでついついDRY, DRY (Do Not repeat Yourself)と言いがちなのですが、Rspecにおいても同じ姿勢でいました。

しかし、テストにおいては「DRYじゃないほうが良い」という意見も少なくはなく、その良し悪しについて議論がありました。

自分とは逆の意見を敢えて言っていただけるという機会がこれまであまりなかったため、自分のやり方を正としがちでしたが、これまでのソースコードレビューで「DRYじゃない!」と指摘してしまっていた自分とは別の考え方があるんだということを学ぶ良いきっかけになりました。

ちなみに、やはりコピペはコピーそのものよりも「コピーした先に変更を加えると、コピー元との差分が見分けづらい」というのが個人的には一番のペインで、膨大な類似コードベースが大の苦手なのです・・・。さらに「複数ある同一のテストのうち一部しか追加改修が行われず、その他の部分でバグが拾えない」というリスクが埋め込まれるのも怖いなぁと思います(個人の意見です)

議論②: 内部のメソッドコールを検証すべきか?

発表内でこのようなことを述べました。

つまり、stub化したメソッドがどういう引数で、何回コールされたか検証するよ!ということです。

具体的にはこのような実装です。

expect(...).to have_received(...).with(...).exactly(...).times

それに対しては「メソッドのINPUT/OUTPUT/副作用(メールやお知らせの送信、DB更新)をテストすれば十分」という意見が多く出ました(おそらく私以外全員?😅)

せっかく私の記事なので私の意見を補足させていただければ、「実装/改修時に一度はコンソールや実機で確認してる(←はず)ことなら、テストコードにしてCIで繰り返し保証されることはむしろ得である」という考えだったのかなと、ディスカッションを終えて思いました。

「1回しか呼ばれないはずのメソッドが、改修によって2回呼ばれるようになってパフォーマンス劣化が生じた」のようなリスクのヘッジがCIで担保できることのメリットを享受できることが良いのでは、と思います。

余談ですが、この手法は自分のエンジニア歴の過去を遡れば、過去にJavaプロジェクトでjUnitやGuice, mockitoでテストを書いていた時に当時のプロジェクトメンバーに教え込まれた方法でした。三つ子の魂百までとはよく言ったものだと感じざるを得ませんでした・・・👶 👴

ディスカッションを終えて

最初に思ったとおり「せっかくだからオレオレスタイルを晒して生の意見を頂こう!」という期待通りのディスカッションができました。凡庸な感想ではありますが、些細でもアウトプットすることでフィードバックを頂けたことで視野を広げられたり、自分の意見をよりクリアにできたのは良い体験だっただと感じました。

また、いろんな主義主張が飛び交っている中「テストを書いている限り、僕たちは正しい」という名言が生まれたことも印象深かったです。意見の相違をより良い状態に昇華させるためには、お互いへのリスペクトが大事ですね。

nikotama.rbは、初級レベルの方でも遠慮なくご参加いただけるイベントなので「他社のエンジニアと交流したい!(できればオンラインで)」「アウトプットをしてみたい!」という方はぜひお気軽にご参加下さい🥺🥺

nikotamarb.connpass.com

あわよくば、ラクマのメンバーと話してみたい!と思っていただければ幸いです!(「話を聞きたい」お待ちしております😉)

www.wantedly.com

www.wantedly.com

www.wantedly.com

One more thing

「オンラインの特性を生かして何かしたいな」と考えて、YouTuberのビデオによくあるフリー効果音やBGMをスライドで流す、ということに挑戦しようとしました(「ドドン」という太鼓の音とかですね)

  • 発表者としては、生声ではなくマイクとスピーカーを通すので、効果音やBGMが邪魔にならないようにコントロールできる
  • オーディエンスにとっても、淡々したと発表を見るよりも、より効果的な演出がある方が楽しめるのではないか
  • 結果、和やかな雰囲気を作って安心してディスカッションができるのではないか

という仮説検証をしようとしたのですが、当日使用したオンラインミーティングツールがこういった用途に対応しておらず、ただただ自分側のイヤフォンから効果音とBGMが流れる中発表せざるを得ませんでした…。

当日使われるツールまで確認して準備に取り掛かるべきでした、という反省を得ました😇