Рубин. Издеваться в RSpec
У меня проблема с издевательством. У меня есть класс DistanceMatrix, и я хотел бы указать, какой метод form_matrix был вызван в операторе if/else. Мне нужно использовать мокко и RSpec. Есть идеи?
class DistanceMatrix
def initialize(*args)
if args[0].class == String
form_matrix(get_data_from_yaml(args[0], args[1]))
elsif args[0].class == Array || args[0] == nil
form_matrix(get_data_from_db(args[0]))
end
end
def form_matrix(...)
...
end
end
он попробовал:
describe DistanceMatrix, "when mocking ..." do
it "should do call form_matrix" do
DistanceMatrix.any_instance.expects(:form_matrix).with([1]).once
DistanceMatrix.any_instance.expects(:get_data_from_yaml).with("file_name.yml").once.returns([1])
DistanceMatrix.new("file_name.yml")
end
end
но получил ошибку:
Failures:
1) DistanceMatrix when mocking ... should do call form_matrix
Failure/Error: DistanceMatrix.new("file_name.yml")
unexpected invocation: #<AnyInstance:DistanceMatrix>.get_data_from_yaml('file_name.yml', nil)
unsatisfied expectations:
- expected exactly once, not yet invoked: #<AnyInstance:DistanceMatrix>.get_data_from_yaml('file_name.yml')
- expected exactly once, not yet invoked: #<AnyInstance:DistanceMatrix>.form_matrix([1])
satisfied expectations:
- allowed any number of times, already invoked once: #<DistanceMatrix:0x9e48b40>.get_optimal_route(any_parameters)
- allowed any number of times, already invoked once: #<Database::Distances:0x9d59798>.load_distances(any_parameters)
# ./distance_matrix.rb:18:in `initialize'
# ./tsp_algorithm_spec.rb:253:in `new'
# ./tsp_algorithm_spec.rb:253:in `block (2 levels) in <top (required)>'
Finished in 0.25979 seconds
Я обнаружил, что в RSpec мы должны использовать не.expected (), а.should_receive (), поэтому я попытался:
describe DistanceMatrix, "when mocking ..." do
it "should do call form_matrix" do
DistanceMatrix.any_instance.should_receive(:form_matrix).with([1])
DistanceMatrix.any_instance.should_receive(:get_data_from_yaml).with("file_name.yml").and_return([1])
DistanceMatrix.new("file_name.yml")
end
end
но получил новый сбой:
Failures:
1) DistanceMatrix when mocking ... should do call form_matrix
Failure/Error: DistanceMatrix.any_instance.should_receive(:form_matrix).with([1])
(#<Mocha::ClassMethods::AnyInstance:0x96356b0>).form_matrix([1])
expected: 1 time
received: 0 times
# ./tsp_algorithm_spec.rb:251:in `block (2 levels) in <top (required)>'
Finished in 0.26741 seconds
2 ответа
У меня есть только опыт использования Mocha, а не RSpec, но, глядя на сообщение о сбое Mocha, можно выделить следующие ключевые моменты:-
unexpected invocation: #<AnyInstance:DistanceMatrix>.get_data_from_yaml('file_name.yml', nil)
unsatisfied expectations:
- expected exactly once, not yet invoked: #<AnyInstance:DistanceMatrix>.get_data_from_yaml('file_name.yml')
Если вы посмотрите на концы этих строк, вы заметите, что get_data_from_yaml
не вызывается с ожидаемыми параметрами. Это вызывается с ('filename.yml', nil)
и не ('filename.yml')
как и ожидалось.
Это происходит потому, что когда вы звоните DistanceMatrix.new("file_name.yml")
в вашем тесте только с одним аргументом, а затем внутри DistanceMatrix#initialize
DistanceMatrix#get_data_from_yaml
вызывается с (args[0], args[1])
и с тех пор args
является массивом из одного элемента, args[1]
будет nil
,
Возможно, это не то, как вы ожидали, что Ruby будет работать, но следующее демонстрирует это поведение:
def foo(*args)
puts "args[0]=#{args[0].inspect}; args[1]=#{args[1].inspect}"
end
foo("string") # => args[0]="string"; args[1]=nil
DistanceMatrix.any_instance.expects(:form_matrix).with("String") # => supply the correct string param
или же
DistanceMatrix.any_instance.expects(:form_matrix).with([]) # => supply the correct array param
Я не уверен, что делают ваши методы get_data_from_db и get_data_from_yaml, но вы должны иметь возможность контролировать эти входные данные, чтобы убедиться, что правильные аргументы передаются в form_matrix.
РЕДАКТИРОВАНИЕ Вам придется использовать DistanceMatrix.any_instance вместо насмешки над переменной экземпляра, потому что вы пытаетесь что-то смоделировать в инициализаторе. Кроме того, в случае, если это неясно, вам нужно будет сделать соответствующий вызов метода после того, как вы установили макет в приведенных выше строках, например
DistanceMatrix.new("SomeString")
РЕДАКТИРОВАНИЕ
it "should do call #form_matrix with proper arguments" do
DistanceMatrix.any_instance.expects(:form_matrix).with([1])
DistanceMatrix.any_instance.expects(:get_data_from_yaml).with("foo").returns([1])
DistanceMatrix.new("foo")
end