📚

【rspec】スーパークラスのテストの書き方

2023/12/03に公開

スーパークラスのテストの書き方が曖昧だったのでChatGPTに聞いてみました。

結論

shared_examplesを使用してスーパークラス(抽象クラス)のテストをサブクラス(具象クラス)にインクルードしてテストする。

前提

  • インスタンスの作成にはFactoryBot使用
  • スーパークラスでabstract=trueを設定しているのでスーパークラスのインスタンスを作成できない(仮にスーパークラスのインスタンスを作成でき、スーパークラスをインスタンス化してテストしても、サブクラスをテストしたことにならない。サブクラスでメソッドをオーバーライドして意図しない挙動になっているかもしれない。)

具体例

スーパークラスとサブクラスの定義

class Vehicle
  self.abstract_class = true

  def start_engine
    'Engine started'
  end
end

class Car < Vehicle
end

スーパークラスのテスト

vehicle_spec.rb
module SharedExamplesVehicle
  RSpec.shared_examples 'a Vehicle' do
    describe '#start_engine' do
      # FactoryBotを使用してインスタンスを作成
      let!(:vehicle_instance) { create(described_class.to_s.underscore.to_sym) }

      it 'starts the engine' do
        expect(vehicle_instance.start_engine).to eq('Engine started')
      end
    end
    
    describe '#xxxx' do
    ...
    end
  end
end

サブクラスのテスト

car_spec.rb
require 'models/vehicle_spec'

RSpec.describe Car, type: :model do
  include SharedExamplesVehicle
  # 以下の記述でスーパークラスのshared_examples内のテストが全て実行される
  it_behaves_like 'a vehicle'
  ...
 
end

ポイント

shared_examples

同様の振る舞いをテストする際に便利です。
詳しくはこちら
https://techtechmedia.com/it_behaves_like-rspec/

described_class.to_s.underscore.to_sym

テスト対象のサブクラスのクラス名を動的に取得して、FactoryBotでインスタンス作成時のファクトリ名として利用します。

  • described_class

テスト対象のサブクラスのクラス名を動的に取得します。

  • クラス名の変換

取得したクラス名(described_class.to_s)をunderscoreメソッドでスネークケースに変換し、さらにto_symでシンボル化します。
この変換により、FactoryBotでのインスタンス生成時に適切なファクトリ名を指定することが可能になります。

  • FactoryBotの設定

FactoryBotで定義したファクトリにおいて、スネークケースのモデル名をaliasesプロパティでエイリアスとして設定します。このエイリアスは、上記のクラス名の変換によって得られた名前と一致している必要があります。
(モデル名のスネークケースをファクトリ名として使っている場合aliasesの設定は不要)
実際に書いたコードではfactory名にモデルの略称を使っていた為、aliasesプロパティでモデル名のスネークケースをエイリアスに設定しました。

FactoryBot.define do
  factory :city_industry_hit, class: 'CityIndustryEntityHit', aliases: [:city_industry_entity_hit] do
    ...
  end
end

あとがき

オブジェクト指向設計実践ガイドでも以下のように記述されていました。
「その共通の契約に共有されるテストを書き、すべてのオブジェクトにそのテストをインクルードすることです。」

参考
Sandi Metz. オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方 (Japanese Edition)

Discussion