Подтвердить что ты не робот

Рубикоп 25 размер блока и RSpec

Типичный RSpec unit test широко использует вложенные блоки Ruby, чтобы структурировать код и использовать "магию" DSL, чтобы иметь спецификации, такие как инструкции BDD:

describe Foo do
  context "with a bar" do
    before :each do
      subject { Foo.new().add_bar }
    end

    it "looks like a baz" do
      expect # etc

В идеальной спецификации каждый пример может быть относительно коротким и точным. Однако обычно кажется, что внешние блоки растут до 100 строк плюс, потому что структура RSpec работает таким образом и не принимает много примеров примеров, каждая из которых может иметь несколько строк конкретной настройки, чтобы добраться до блоков describe которые имеют тот же размер или больше, чем код для описываемого объекта.

Недавняя модернизация Rubocop привела к появлению нового правила, что блоки должны быть не более 25 строк. Я не уверен в его обосновании, потому что он не указан в Ruby style guide. Я могу понять, почему это может быть хорошо, и добавил к набору правил по умолчанию. Однако после обновления наш тест Rubocop несколько раз повторяется с сообщениями типа tests/component_spec.rb:151:3: C: Block has too many lines. [68/25]

С инструментами метрики кода, такими как Rubocop, мне нравится иметь политику "Использовать значения по умолчанию, ссылку на руководство по стилю, выполненную работу". (главным образом потому, что дебатирование вкладок против пробелов и других мелочей тратит время, а IME никогда не разрешается). Здесь это явно невозможно, два наших основных инструмента качества данных не согласны с подходом кодового макета - или, по крайней мере, так я интерпретирую результаты, Я не вижу ничего по-настоящему неправильного в том, как мы написали спецификации.

В ответ мы просто установили правило размера блока Rubocop на высокий порог. Но это заставляет меня задаться вопросом - чего мне не хватает? Является ли RSpec использующим теперь дискредитированный подход для компоновки кода и какие разумные варианты мне нужно уменьшить размеры блоков в наших тестах RSpec? Я вижу способы реструктуризации кода, чтобы избежать больших блоков, но они без исключения уродливые хаки, предназначенные исключительно для соответствия правилам Rubocop, например. разбить все блоки на вспомогательные функции:

def looks_like_a_baz
  it "looks like a baz" do
         expect # etc
  end
end

def bar_context
  context "with a bar" do
    before :each do
      subject { Foo.new().add_bar }
    end
    looks_like_a_baz
  end
end


describe Foo do
  bar_context
  # etc

., Я имею в виду, что do-able, но превращение пучков примеров spec в вспомогательные функции таким образом, похоже, противоположно читаемому подходу, разработанному дизайном RSpec.

Есть ли что-нибудь еще, что я могу сделать, кроме как найти способы игнорировать его?


Ближайший существующий вопрос, который я мог найти по этой теме, был RSpec и Rubocop/Ruby Style Guide, и это выглядело разрешимым путем редактирования тестовых шаблонов.

4b9b3361

Ответ 1

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

Раньше все копы были основаны на Руководстве по стилю Ruby, а RuboCop был способом придерживаться практики, установленной сообществом.

С тех пор направление изменилось, и область применения RuboCop расширилась, чтобы помочь разработчикам обеспечить согласованность своих основ кода в целом. Это привело к двум вещам:

  1. Копы (даже те, которые основаны на Руководстве по стилю Ruby) теперь в основном настраиваются.
  2. Есть копы для вещей, которые не упомянуты в Руководстве по стилю Ruby, но все же полезны для обеспечения согласованности в проекте.

Этот полицейский попадает во вторую категорию.

Использует ли RSpec теперь дискредитированный подход для компоновки кода, и какие разумные варианты у меня есть, чтобы уменьшить размеры блоков в наших тестах RSpec?

Краткий ответ - нет. DSL по-прежнему круто. :-)

Этот полицейский нацелен на большой блок в смысле императивного программирования. Как общее руководство, оно не применяется к DSL, которые часто являются декларативными. Например, наличие длинного файла routes.rb в Rails совершенно удобно. Это просто естественный результат большого приложения, а не нарушение стиля. (И много тестов - это просто здорово.)

RuboCop довольно умен, но он не знает, что такое DSL и нет, поэтому мы не можем автоматически их игнорировать. Можно утверждать, что мы могли бы исключить методы входа DSL из популярных фреймворков, таких как маршруты Rails и спецификации RSpec. Причины для того, чтобы этого не делать в первую очередь:

  1. Ложные негативы. Любой класс может реализовать метод с использованием блока с тем же именем.
  2. RuboCop - это инструмент анализа Ruby, и он не должен знать о внешних библиотеках. (Исключение каталога /spec - это вежливость, пока у нас не будет правильной системы расширений, и это может быть обработано rubocop-rspec.)

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

Суть в том, что RuboCop поможет нам написать лучший код. Если дизайн нашего приложения иным образом продуман, и мы делаем вещи менее читабельными, просто чтобы угодить RuboCop, тогда мы должны отфильтровать, настроить или отключить копа. :-)

В ответ мы просто установили для правила размера блока Rubocop высокий порог. Но это заставляет меня задуматься - чего мне не хватает?

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

  1. Файлы, которые содержат чисто декларативные DSL, например, маршруты Rails, спецификации RSpec.
  2. Файлы, имеющие декларативный DSL, смешанные в основном с императивным кодом, например, объявление aasm состояния aasm в модели Rails.

В первом случае лучшим решением является исключение файла или каталога, а во втором - использование встроенного отключения.

В вашем случае вы должны обновить ваш .rubocop.yml:

Metrics/BlockLength:
  Exclude:
    - 'Rakefile'
    - '**/*.rake'
    - 'test/**/*.rb'

(Обратите внимание, что вам нужно повторить базовые исключения из конфигурации по умолчанию, так как список будет перезаписан.)

Ответ 2

Если определенный блок обычно слишком длинный, я указываю его, а не файлы

Metrics/BlockLength:
  ExcludedMethods: ['describe', 'context']