Rubocop 25行块大小和RSpec测试


70

典型的RSpec单元测试大量使用嵌套的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样式指南中列出。我可以看到为什么这会是一件好事,并将其添加到默认规则集中。但是,升级后,我们的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

。。。我的意思是,这是可行的,但是以这种方式将大量规范示例转换为辅助函数似乎与RSpec设计鼓励的可读方法相反。

除了找到忽略它的方法以外,我还能做其他事情吗?


我在这里可以找到的关于该主题的最接近的现有问题是RSpec&Rubocop / Ruby Style Guide,通过编辑测试模板可以解决。


3
默认设置排除下的文件spec/
Stefan

1
@Stefan:啊,所以我们的使用使我们test/暴露了这一点。。。很高兴知道。这意味着Rubocop作者承认RSpec代码有所不同,我们也应该如此。
尼尔·斯莱特

Answers:


91

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-rspecgem处理。)

我的意思是,这是可行的,但是以这种方式将大量的规范示例转换为辅助函数似乎与RSpec设计鼓励的可读方法相反。

底线是:RuboCop可以帮助我们编写更好的代码。如果我们的应用程序设计听起来不错,而我们发现自己只是为了取悦RuboCop而使事情变得不那么可读,那么我们应该过滤,配置或禁用cop。:-)

作为响应,我们仅将Rubocop块大小规则设置为高阈值。但这让我感到奇怪-我想念什么?

这是一个相当钝的工具,正如您所暗示的那样,您可能会因此而产生一些误报。该警察的误报有两种类型:

  1. 包含纯声明性DSL的文件,例如Rails路由,RSpec规范。
  2. 具有声明性DSL的文件混入了大多数命令性代码中,例如aasm,Rails模型中的状态机声明。

在第一种情况下,最好的解决方案是排除文件或目录,在第二种情况下使用内联禁用。

对于您的情况,应使用以下命令进行更新.rubocop.yml

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

(请注意,由于该列表将被覆盖,因此您需要从默认配置中再次重申基本排除项。)


129

如果特定的块通常太长,我指定它而不是文件

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

7
更确切地说,取决于块名而不是文件名,所以我认为这个答案更好。
sekrett

1
ExcludedMethods已重命名为IgnoredMethods
Mikey,2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.