動的クラス
クラスが存在しないメソッドをサポートしていないことを確認するために、インスタンスのダブルは検証できません。
実際のクラスのインスタンスが必要なためです。これは、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
を実行すると、
すべての例がパスするはずです。