月額9,800円から通えるプログラミングスクール

【Rails】Rspec+circleCIでテストを自動化しよう!テスト駆動開発のメリットとは?

この記事ではRailsAPIをRspecとcircleCIで自動テストする方法を紹介します。

読者

Railsのテストを自動化したい!簡単な実装方法を教えて欲しい!そもそもテストを書く必要ってあるの?テスト駆動開発のメリットを教えて欲しい!

そのような要望にお答えします!

経緯

弊社サービスは今までテストを全て手動で行なっていたのですが、細かいコード修正でもいちいちテストするのが面倒になってきたのでテストを自動化させようというお話しになりました。

というか自分が提案しました。多分提案しなければこの先も永遠と手動でテストしていたと思います…

皆さんの会社ではどうですか?テスト書いてますか?自動化してますか?

今時自動テストを導入してない会社なんてないんじゃね…?と自分は思ってますが実際どうなんでしょう。

テストを書くメリット

昔はテスト書かなくて良い派だったんですけど、今は書くべきだと思ってます。

テストを書くメリットは主に2つあります。

  • テストコードが仕様書になる
  • 長い目で見ると実装時間を大幅に削減できる

テストコードが仕様書になる

弊社はRailsをAPIとして使っていますが、人によってレスポンスの書き方がバラバラだったり返すべきものが間違っていたり統一感のないコードになっていました。(現在少しずつ修正しています)

例えばパラメータが不足していてエラーになる場合は400(不正なリクエスト)を返すべきなのに何も処理が施されておらず、コードエラー500が返る仕様になっていたり。

そういった細かな仕様をテストコードを書く時に決めておくことで、誰しもが最低限の正しい実装をするようになります。

実装前にテストを書いておくことで、何を返すべきなのか?どうするべきなのか?迷った時にテストを見れば分かるようになるので絶対テストコードは書いた方が良いと思います。

ただテストを書く分の工数はかかるので、プロジェクトによっては先に実装してからテストを書くというパターンもありますし、レアケースですが弊社のようにベンチャーで人が足りていない場合やスピード命!みたいなプロジェクトでは書かないこともあるみたいです。

実装時間を大幅に削減できる

自分はサーバーサイド担当でRailsのAPIを書くことが多いのですが、実装は速攻で終わってもテストでその何倍も時間をかけることが多かったです。

例えば検索機能で全パターン試す…なんて時はまじで面倒です。

テストコードは最初の工数がかかりますが、書いてしまえばそれ以降は自動化できるので長期的に見ると大幅な工数削減になります。

実際導入して1ヶ月ぐらい経ちますがめちゃくちゃ楽です。

ほろよい

もっと早くやっておけば良かった…

それでは実際に導入した時の手順を以下にまとめておきます。

環境

  • ruby 2.4.3
  • Rails 5.1.6.1

Rspecの導入

gemの追加

$vim Gemfile group :test do
 gem 'rspec-rails'
 gem 'factory_bot_rails'
end

bundle install

$bundle install

設定ファイルの生成

$bundle exec rails g rspec:install
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb

設定ファイルの編集

$vim .rspec
require --rails_helper 
$vim spec/spec_helper.rb
RSpec.configure do |config|
 #factory_botを使用するための設定を追加する
  config.before(:all) do
    FactoryBot.reload
  end
end

テスト用データの意義

テストを始めるにはあらかじめデータが入っていなければなりません。

テスト用データのモデルを作ります。

$vim spec/factories/users.rb
FactoryBot.define do
    factory :user do
        name{"テストユーザ"}
        auth_token{"auth_token"}
    end
end
$vim spec/factories/feed_posts.rb

FactoryBot.define do
    factory :feed_post do
        :user
        description{"テスト"}
    end
end

テスト用ファイルを生成

$mkdir -p spec/requests/api
$touch spec/requests/api/feed_posts_spec.rb
$vim spec/requests/api/feed_posts_spec.rb
describe "#show" do
  let(:post){create(:feed_post, user: user)}
  context "全てのパラメータが揃っている場合" do
    it "200を返す" do
      get api_feed_post_path(post.id), params: {auth_token:"auth_token", version: "ios_3.7.0"}
      expect(response.status).to eq(200)
      expect(JSON.parse(response.body).keys).to include("post_info","comment_info","user_info")
    end
  end

  context "全てのパラメータが揃っているが、フィードが存在しなかった場合" do
    it "404を返す" do
      get api_feed_post_path(0), params: {auth_token:"auth_token", version: "ios_3.7.0"}
      expect(response.status).to eq(404)
    end
  end

  context "パラメータが不足している場合" do
    it "400を返す" do
      get api_feed_post_path(post.id), params: {auth_token:"auth_token"}
      expect(response.status).to eq(400)
    end
  end
end

テスト実行

$bundle exec rspec spec/requests/api/feed_posts_spec.rb

circleCIの導入

GithubとcircleCIの連携

こちらhttps://circleci.com/signup/の「Sign up with GitHub」から登録します。

最初の方のセットアップはちょっと覚えてないので他記事を参考にしてください。でも確か特に設定は無かったと思います。

左のサイドバー「Add Project」からcircleCIと連携したいリポジトリを追加します。

circleCIで必要になるgem追加

group :test do
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem "rspec_junit_formatter" //追加
end

bundle install

$bundle install

.circleci/config.ymlの作成

version: 2
jobs:
  build:
    docker:
      - image: circleci/ruby:2.4.4-node-browsers
        environment:
          BUNDLER_VERSION: 2.0.2
          RAILS_ENV: test
      - image: circleci/mysql:5.7.22

    working_directory: ~/repo

    steps:
      - checkout

      - run:
          name: setup bundler
          command: |
            sudo gem update --system
            sudo gem install bundler -v 2.0.2
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-dependencies-

      - run:
          name: install dependencies
          command: |
            bundle install --path vendor/bundle
      - save_cache:
          paths:
            - ./vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}

      - run: mv config/database.yml.ci config/database.yml 

      - run:
          name: database create
          command: |
            dockerize -wait tcp://127.0.0.1:3306 -timeout 120s
            bundle exec rake db:create
            bundle exec rake db:schema:load
      - run:
          name: run tests
          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
              circleci tests split --split-by=timings)"
            bundle exec rspec \
              --format progress \
              --format RspecJunitFormatter \
              --out /tmp/test-results/rspec.xml \
              $TEST_FILES
      - store_test_results:
          path: /tmp/test-results

      - store_artifacts:
          path: /tmp/test-results
          destination: test-results

database.yml.ciの作成

test:
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: 'root'
  port: 3306
  host: '127.0.0.1'
  database: ci_test

test_slave:
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: 'root'
  port: 3306
  host: '127.0.0.1'
  database: ci_test 

Gitにプッシュ

これでリポジトリにプッシュする度、circleCI上でテストが実行されます!

まとめ

設定ファイルの書き方で少しつまづきましたが、日本語の公式ドキュメントがあるのでなんとかなりました。

終わってみると実装自体はむちゃくちゃ簡単だなーって感じです。

未経験でポートフォリオを作っている人はテスト自動化してみると面接で評価してもらえるかもしれないのでやってみることをお勧めします!

ほろよい

今回は以上になります。次回は自動デプロイについて書こうと思ったり思わなかったりラジバンダリ。もしこの記事が参考になったら拡散してくれると嬉しいです٩( ᐛ )و

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です