ActiveSupport::Concernの使い方とテスト(RSpec)

  • 60
    いいね
  • 0
    コメント

何回も書くことないかなと思ってましたが、Concernsのテスト書くことになって前に書いたやつ見に行ったりしたのでこの際自分に都合のいい形でまとめておきました。

References

Concernの使い方

モデル間で共有したいmoduleを下記のように定義

app/models/concerns/naming.rb
# frozen_string_literal: true

module Naming
  extend ActiveSupport::Concern

  DEFAULT_NAME = "Default Name"

  # scope, callback, relation はここに定義  
  included do
    before_create :assign_default_name, unless: :name?

    scope :with_name, -> (names) { where(name: names) }
  end

  # class メソッド
  class_methods do
    # cattr_reader 使ってもいいですが例なのであえて冗長に書いてます
    def default_name
      DEFAULT_NAME
    end
  end

  def assign_default_name
    self.name = DEFAULT_NAME
  end
end
app/models/user.rb
# frozen_string_literal: true

class User < ApplicationRecord
  include Naming
end

テスト(RSpec)

spec/rails_helper.rb
# ...
require 'rspec/rails'

# spec/support/ 以下のファイルがちゃんと require されるようになってるか確認
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
spec/support/models/concerns/naming_helper.rb
# frozen_string_literal: true

# 別に module 宣言なくいきなり shared_example_for から始めても
# 問題ないです。
module NamingHelper
  shared_examples_for 'naming' do
    let(:object_name) { described_class.to_s.underscore.to_sym }

    # FactoryGirl入れてる前提でいます  
    let(:model) { build(object_name) }

    # 普通にテスト書くのと同じようにテストを書きます
    describe ".default_name" do
       subject { described_class.default_name }

       it { is_expected.to eq(described_class::DEFAULT_NAME) }
    end

    describe '#assign_default_name' do
      context "w/ name" do
        before do
          model.name = "user1"
          model.save!
        end

        it "not assign default name" do
          expect(model.name).to_not equal(described_class::DEFAULT_NAME)
        end
      end

      context "w/o name" do
        before do
          model.name = ""
          model.save!
        end

        it "assigns default name" do
          expect(model.name).to equal(described_class::DEFAULT_NAME)
        end
      end
    end
  end
end
spec/models/user_spec.rb
# frozen_string_literal: true

require 'rails_helper'

describe User, type: :model do
  # これを書くだけ
  it_behaves_like 'naming'
end

これで user_spec.rb が実行される時に Naming のテストケースが呼ばれ、無事テストもDRYでいい感じに書けました。

ただ shared_examples は使いすぎるとテストが読みづらくなるので、テストに関しては過度に DRY にしすぎず、読みやすさ重視で書いた方がいいと個人的には思っています :smiley: :spades:

いろんなところを参考にしました :pray: