Рубин. Издеваться в 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#initializeDistanceMatrix#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
Другие вопросы по тегам