Рубин. Насмешки в 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 мы должны использовать не .expects(), а .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

person Martynas    schedule 01.12.2010    source источник
comment
#should_receive использует насмешку rspec — изначально вы сказали, что используете мокко. Если вы хотите использовать мокко, вы должны настроить это в spec_helper.rb для вашего проекта.   -  person Keith Gaddis    schedule 01.12.2010
comment
Обе эти ошибки говорят вам о том, что form_matrix не была вызвана, как вы ожидали. Я не думаю, что проблема в вашем тесте. Я предполагаю, что то, что вы передаете в инициализатор, представляет собой обернутую строку или что-то в этом роде, поэтому args[0].class != String. Вы прошли через это в отладчике, чтобы понять, что происходит?   -  person Keith Gaddis    schedule 01.12.2010
comment
Нет. Как я могу отлаживать терминал Linux?   -  person Martynas    schedule 02.12.2010


Ответы (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
person James Mead    schedule 04.12.2010

DistanceMatrix.any_instance.expects(:form_matrix).with("String") # => supply the correct string param

or

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
person Keith Gaddis    schedule 01.12.2010
comment
мои оба метода get_data_from_yaml и get_data_from_db возвращают массив. Я пробовал это: описать DistanceMatrix, когда насмехается... сделать это следует... сделать Distance_matrix = DistanceMatrix.any_instance Distance_matrix = mock Distance_matrix.expects(:form_matrix).with(an_instance_of(Array)) DistanceMatrix.new(file_name.yml, [1, 2, 3]) конец конец, но это не работает. - person Martynas; 01.12.2010
comment
Хорошо, в таком случае я бы смоделировал методы get_data_from_yaml и get_data_from_db, чтобы они возвращали два известных значения и устанавливали ваши ожидания относительно этих известных значений. см. правку выше - person Keith Gaddis; 01.12.2010
comment
Я изменил ожидания на should_receive и получил новый сбой. см. правку выше. - person Martynas; 01.12.2010