メインコンテンツまでスキップ

動的クラス

クラスが存在しないメソッドをサポートしていないことを確認するために、インスタンスのダブルは検証できません。 実際のクラスのインスタンスが必要なためです。これは、method_missing が使用されている場合によく起こります。

これを回避する方法はいくつかあります。オブジェクトが既にロードされている場合は、object_doubleを使用することを検討することができますが、それは孤立してテストしている場合には機能しません。 また、直接メソッドを実装することもできます(super を呼び出して method_missing の定義を返す)。

これらのクラスの一部には、ランタイムでオブジェクトにこれらのメソッドを定義するためのメソッドがある場合があります。 (たとえば、ActiveRecord はデータベースの列からメソッドを定義するためにこれを行います。)これらの場合、作成時に検証ダブルをカスタマイズするために使用できる API を提供しています。 私たちは rspec-rails でこれを使用して、いくつかの便利な機能を設定しています。

これらのタイプのメソッドはクラスレベルでサポートされています(class_double を使用)が、respond_to? は直接クラスにクエリできるためです。

背景

次の内容で "lib/fake_active_record.rb" という名前のファイルを作成します。

class FakeActiveRecord
COLUMNS = %w[name email]

def respond_to_missing?(method_name)
COLUMNS.include?(method_name.to_s) || super
end

def method_missing(method_name, *args)
if respond_to?(method_name)
instance_variable_get("@#{method_name}")
else
super
end
end

def self.define_attribute_methods
COLUMNS.each do |name|
define_method(name) { instance_variable_get("@#{name}") }
end
end
end

次の内容で "spec/user_spec.rb" という名前のファイルを作成します。

require 'user'

RSpec.describe User do
it 'can be doubled' do
instance_double("User", :name => "Don")
end
end

method_missing で失敗する場合

次の内容で "lib/user.rb" という名前のファイルを作成します。

require 'fake_active_record'

class User < FakeActiveRecord
end

rspec spec/user_spec.rb を実行すると、

出力に "1 example, 1 failure" が含まれているはずです。

明示的な定義を使用した回避策

次の内容で "lib/user.rb" という名前のファイルを作成します。

require 'fake_active_record'

class User < FakeActiveRecord
def name; super end
def email; super end
end

rspec spec/user_spec.rb を実行すると、

すべての例がパスするはずです。

コールバックを使用した回避策

次の内容で "lib/user.rb" という名前のファイルを作成します。

require 'fake_active_record'

class User < FakeActiveRecord
end

次の内容で "spec/fake_record_helper.rb" という名前のファイルを作成します。

RSpec.configuration.mock_with(:rspec) do |config|
config.before_verifying_doubles do |reference|
reference.target.define_attribute_methods
end
end
#
# or you can use:
#
# RSpec::Mocks.configuration.before_verifying_doubles do |reference|
# reference.target.define_attribute_methods
# end

rspec -r fake_record_helper spec/user_spec.rb を実行すると、

すべての例がパスするはずです。