RSpec - как проверить, был ли вызван метод другого класса? - PullRequest
1 голос
/ 07 ноября 2019

Я хочу, чтобы обезьяна исправила метод выбора String #, и хочу создать набор тестов, который проверяет, что он не использует Array # select.

Я попытался создать группу тестов, используя to_not receive, используя to_not receive(Array:select) и просто to_not receive(:select). Я также попытался использовать массив (string.chars) вместо строки. Гугл и переполнение стека не принесли ответа.

describe "String#select" do
  it "should not use built-in Array#select" do
    string = "HELLOworld".chars
    expect(string).to_not receive(Array:select)
  end
end

Ожидается: рабочий набор тестов, который проверяет, что метод Array # не использовался во всем методе.

Фактический вывод: получаю ошибку, что недостаточно аргументовбыл использован. Выходной журнал ниже:

  1) RECAP EXERCISE 3 Proc Problems: String#select should not use built-in Array#select
     Failure/Error: expect(string).to_not receive(Array:select)
     ArgumentError:
       wrong number of arguments (given 0, expected 1..4)
     # ./spec/problems_spec.rb:166:in `select'
     # ./spec/problems_spec.rb:166:in `block (4 levels) in <top (required)>'

1 Ответ

1 голос
/ 11 ноября 2019

Прежде всего: тесты должны проверять результаты вызванных методов, а не способ их реализации. Полагаясь на это, вы попадете в беду.

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

Допустим, что String#select использует Array#select для внутреннего использования, а последнееглючит при некоторых обстоятельствах. Лучше сделать тест, настроив юниверс таким образом, чтобы он вызывал ошибку и проверял, что поведение ошибки отсутствует. Затем исправьте String#select и проверьте зеленый цвет. Это намного лучший подход, потому что тест теперь говорит всем , почему вы не должны использовать Array#select для внутреннего использования. И если ошибка устранена, под солнцем проще всего удалить патч и проверить, что спецификация все еще зеленая.

При этом, если вам все еще нужно, вы можете использовать expect_any_instance_of, чтобы подтвердить это, например, эта спецификация потерпит неудачу:

class String
  def select(&block)
    split.select(&block) # remove this to make the spec pass
  end
end

specify do
  expect_any_instance_of(Array).not_to receive(:select)

  'foo'.select
end

Если вы не хотите использовать expect_any_instance_of ( по причинам ), вы можете временно перезаписать метод в классе для сбоя:

class String
  def select(&block)
    #split.select(&block)
  end
end

before do
  class Array
    alias :backup_select :select

    def select(*)
      raise 'No'
    end
  end
end

after do
  class Array
    alias :select :backup_select # bring the original implementation back
  end
end

specify do
  expect { 'foo'.select }.not_to raise_error
end

Псевдоним необходим для возврата к исходной реализации, так что вы не путаетесьдо спецификации, которая запускается после этой.

Но вы можете видеть, насколько сложен и запутан этот подход.

В любом случае - то, что вы пытаетесь достичь, - это , наиболее вероятно, проблема дизайна, но трудно сказать без подробностей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...