Неверный метод Stub в rspec

Я тестирую свой модуль, и я решил проверить его по сравнению с анонимным классом:

subject(:klass) { Class.new { include MyModule } }

MyModule использует метод name внутри klass. Чтобы мои спецификации работали, мне нужно заглушить этот метод name (который не реализован). Поэтому я написал:

subject { klass.new }
allow(subject).to receive(:name).and_return('SOreadytohelp') }

но он поднимает:

RSpec::Mocks::MockExpectationError: #<#<class:0x007feb67a17750>:0x007feb67c7adf8> does not implement: name
from spec-support-3.3.0/lib/rspec/support.rb:86:in `block in <module:support>'
</module:support></class:0x007feb67a17750>

как заглушить этот метод без его определения?

3 ответа

RSpec вызывает это исключение, потому что не полезно заглушить метод, который не существует на исходном объекте.

Методы mocking всегда подвержены ошибкам, потому что макет может вести себя иначе, чем исходная реализация, и поэтому спецификации могут быть успешными, даже если исходная реализация вернет ошибку (или даже не существует). Разрешить фальсифицировать несуществующие методы просто неверно.

Поэтому я бы сказал, что вы не должны пытаться обойти это исключение. Просто добавьте метод name к вашему классу, который вызывает четкое исключение, если оно выполняется за пределами тестовой среды:

def self.name
 raise NotImplementedError # TODO: check specs...
end


subject(:klass) do 
 Struct.new(:name) do
 include MyModule
 end
end

http://ruby-doc.org/core-2.2.0/Struct.html


Я думаю, что если тест, который вы пишете, сосредоточен на вашем модуле MyModule, и этот модуль полагается на метод экземпляра в классе, в который он смешался, тогда я думаю, что этот метод должен быть изделен в анонимный класс, который вы используете при тестировании модуля. Например:

module MyModule
 def call_name
 # expected implementation of #name to be
 # in the class this module is mixed into
 name
 end
end
RSpec.describe MyModule do
 let(:my_module_able) do
 Class.new do
 include MyModule
 # We don't care what the return value of this method is;
 # we just need this anonymous class to respond to #name
 def name
 'Some Name that is not SOReadytohelp'
 end
 end.new
 end
 describe '#call_name' do
 let(:name) { 'SOReadytohelp' }
 before do
 allow(my_module_able).to receive(:name).and_return(name)
 end
 it 'returns the name' do
 expect(my_module_able.call_name).to eq(name)
 end
 end
end

licensed under cc by-sa 3.0 with attribution.