😺

RSpecを使って安全に開発|Offers Tech Blog

2022/04/21に公開

こんにちは!Offers を運営している株式会社 overflow の花粉が辛いバックエンドエンジニアのShunです。

お恥ずかしい話ですが、以前私はテストを書くのが嫌で嫌で仕方ありませんでした笑
とにかく早くリリースして価値提供したいという欲が先行し、余計なタスクを振られている気分で...
しかし、今ではテスト無くしてリリースは怖いです。
Production リリース後に致命的なエラーが出てしまったら、revert したり謝罪したり、結果的にマイナスの価値提供を迅速に行ったという痛い思い出しか残りません。。。

なので、今回はテスト嫌いだった私がテスト記述のメリット・デメリットについて話せればなと思います。

RSpecとは

RSpecとは、Ruby 用のテストフレームワークです。

以下のような簡単なメソッドでも、テストを書いておくとメソッドの動作を保証できます。
下記のサンプルコードは、DB から姓名を取得して、フルネームを返却する簡単なものですが、ちゃんとフルネームが表示されているのをわざわざブラウザで確認するのは億劫ですよね...(レスポンスが遅いサイトの開発時とかはストレスがたまる...)

user.rb
class User < ApplicationRecord
  def full_name
    "#{self.last_name} #{self.first_name}"
  end
end
user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'full_name' do
    let(:user) { create(:user, first_name: '太郎', last_name: '田中') }
    it 'フルネームを取得すること' do
      expect(user.full_name).to eq '田中 太郎'
    end
  end
end

テストを書くことのメリット

他にもたくさんあると思いますが、個人的に恩恵を感じているのは以下です。

  • 新規テーブル作成などを含む実装の場合、factory にてダミーデータを作成してテストができるので、わざわざブラウザや rails console からデータ create をしなくてもいい
    # usersテーブルを作成したが、まだ実データは存在しない場合
    describe 'update_user_name' do
      let_it_be(:user) { create(:user) }
      it '名前を変更すること' do
        user.update_user_name(new_name: "hogehoge")
        expect(user.reload.name).to eq "hogehoge"
      end
    end
    
  • テストコードを見ることで、その機能の仕様がわかるので、目視でも影響範囲の確認がしやすい
    # シナリオテストなどの時に、機能の全体像を把握しやすい
    describe 'メッセージ予約シナリオ' do
      let_it_be(:user) { create(:user) }
      let_it_be(:user2) { create(:user) }
    
      context '過去の時間で予約を試みている時' do
        it 'raiseエラーが発生すること' do
          expect{
            user.reserve_message(
              target_user_id: user2.id,
              text: 'おはよう',
              scheduled_at: Time.zone.now.ago(1,month)
            )
          }.to raise_error('過去の日時での予約はできません。')
        end
      end
    
      context '送信予定のユーザが退会した場合' do
        it '現在の予約件数が1減ること' do
          expect(user.current_reserve_messages.length).to eq 2
          user2.quit_service! # 「この退会メソッド叩かれた時に、関連する予約データは破棄される」という気づきがある。
          expect(user.current_reserve_messages.length).to eq 1
        end
      end
    end
    
  • API のレスポンスがテストでちゃんと予期したものになっていれば、フロントに組み込む際にとても楽になる
  • 様々なユースケースを考慮したテストを書いていくため、「あ、このパターン忘れてた。PdM に相談しよう」という発見がある
  • 全てのテストが通った上でリリースしたほうが、精神状態が良い
  • コードレビュー時、メソッドがテストで保証されている前提で取り組めるのでレビューコストが削減される
  • 既存の処理を絡めた開発時、予期せぬエラーなどを事前に回避できる

既存の処理を絡めた開発時、予期せぬエラーなどを事前に回避できる

これは特に恩恵を感じています。
プロダクトは最初は小さく、成長していくにつれコード量もガンガン増えていきます。その際、前まで動いてた機能がバグってるなんてことは誰しも体験したことがあると思われます。エンジニア各々がしっかり自分が実装した機能のテストを記述することで、影響範囲の自動チェックを実現可能にするため、新規開発時にヒヤヒヤせずに開発を進めることができます。

テストを書くことのデメリット

恩恵とのトレードオフではありますが、個人的に感じているデメリットは以下です。
安全な状態でリリースするほうが結果的に良いので、以下は仕方ないことかなとも思ったり。

  • テストコードを記述する時間が増える
  • テストコード記述に慣れてないとキャッチアップに時間がかる
  • RSpec における非推奨の記述などはウォッチ並びに気を遣う必要あり
  • 機能追加によって、既存のテストコードを修正する工数が発生
  • テストファイル内の記述が fat になりがちなので、多少工夫が必要
  • RSpec もプログラムなので、実行速度に気を使わないとリリースまでのリードタイムが増える

テストを書き始めてから良くなった点

  • エンジニアの人数が増えても安定した機能リリースが実現できている
  • バグ対応など、0 ではないが細かい対応で済むため、計画的にスプリントを進められるようになった
  • テストカバレッジ[1]を高い値に維持する意識がチーム内部で浸透する
  • TDD で API 内部のメソッドテストや Unit テストを行うことで、それまで見えなかった課題やケアレスミスが激減した

まとめ

工数的な問題はあるかもしれませんが、サービスを利用するエンドユーザに快適に利用していただくためにも、テスト記述はかなり重要なポイントを担っていると思います!
テストを書きまくることをお勧めします!

関連記事

https://zenn.dev/offers/articles/20220411-open-api-schema
https://zenn.dev/offers/articles/20220328-promote-communication-in-remote-team
https://zenn.dev/offers/articles/20220322-hello-offers-tech-blog

脚注
  1. テスト対象ファイルのうち、どの程度テスト記述がされているかを算出したもの。 ↩︎

Offers Tech Blog

Discussion