典型的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

在理想的规范中,每个示例都可以相对简短和精确.然而,通常情况下,外部块会增加到describe多行,因为RSpec struct 是这样工作的,并且不会采取很多规范示例(每个示例可能有几行特定设置)来获得describe个与所描述主题的代码大小相同或更大的块.

最近对Rubocop的升级带来了一条新规则,即区块长度不得超过25行.我不确定这样做的理由,因为它没有被列入Ruby style guide强.我明白了为什么这可能是一件好事,并添加到了默认规则集.然而,升级后,我们的Rubocop测试多次失败,出现tests/component_spec.rb:151:3: C: Block has too many lines. [68/25]这样的消息

使用Rubocop等代码度量工具,我希望有一个"使用默认值、链接到样式指南、完成工作"的策略(主要是因为讨论制表符与空格以及其他细节是在浪费时间,IME never得到了解决)在这里,这显然是不可能的,我们的两个核心数据质量工具对代码布局方法存在分歧——或者至少这是我解释结果的方式,我认为我们编写规范的方式没有本质上的错误.

作为回应,我们只需将Rubocop块大小规则设置为一个高阈值.但这让我想知道——我错过了什么?RSpec使用的是现在不可信的代码布局方法吗?在我们的RSpec测试中,我有哪些reasonable个选项可以减少块大小?我可以找到重组代码的方法,以避免出现大的代码块,但它们毫无例外都是丑陋的黑客行为,纯粹是为了满足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个,这看起来可以通过编辑测试模板来解决.

推荐答案

最近对Rubocop的升级带来了一条新规则,即区块长度不得超过25行.我不确定它的原理,因为它没有在Ruby样式指南中列出.

过go ,所有的警察都是基于Ruby风格的指南,RuboCop是一种遵守社区规定的实践的方式.

从那时起,方向发生了变化,RuboCop的范围已经扩大,以帮助开发人员确保其代码库总体上的一致性.这导致了两件事:

  1. COP(甚至那些基于Ruby风格指南的)现在基本上都是可配置的.
  2. 对于Ruby风格指南中没有提到的事情,有一些COP,但对于在项目中加强一致性仍然很有用.

这个警察属于第二类.

RSpec使用的是现在不可信的代码布局方法吗?在我们的RSpec测试中,我有什么合理的 Select 来减少块大小?

简而言之,答案是否定的.DSL仍然很酷.:-)

该cop针对命令式编程意义上的大型块.作为一般指南,它不适用于DSL,DSL通常是声明性的.例如,在Rails中有一个长routes.rb文件是完全良性的.这只是大型应用程序的自然结果,而不是风格冲突.(而且有很多测试简直太棒了.)

现在,RuboCop非常智能,但它不知道什么是DSL,所以我们不能自动忽略它们.有人可能会说,我们可以排除流行框架的DSL入口方法,比如Rails routes和RSpec规范.不这样做的原因主要是:

  1. 假阴性.任何类都可以用相同的名称实现一个方法,使用一个块.
  2. RuboCop是一个Ruby分析工具,不应该真正了解外部库.(在我们有合适的扩展系统之前,排除/spec目录是一种礼貌,这可以由rubocop-rspec gem处理.)

我的意思是,这是可以做到的,但以这种方式将一堆规范示例转化为辅助函数似乎与RSpec设计鼓励的可读方法相反.

底线是:RuboCop可以帮助我们编写更好的代码.如果我们的应用程序设计在其他方面是合理的,并且我们发现自己只是为了取悦RuboCop而降低了可读性,那么我们应该过滤、配置或禁用cop.:-)

作为回应,我们只需将Rubocop块大小规则设置为一个高阈值.但这让我想知道——我错过了什么?

这是一个相当直截了当的工具,正如你所暗示的,你可能会因此产生一些错误的否定.此cop有两种类型的误报:

  1. 包含纯声明性DSL的文件,例如Rails路由、RSpec规范.
  2. 将声明性DSL混合到大多数命令式代码中的文件,例如Rails模型中的aasm状态机声明.

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

在您的情况下,您应该使用以下内容更新您的.rubocop.yml:

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

(请注意,您需要重新迭代默认配置中的基本排除,因为列表将被覆盖.)

Ruby相关问答推荐

Ruby:一个方法可以返回不同类型的对象吗?

多线程期间的 MRI ruby​​ 内存访问特性

创建线程安全的临时文件名

Ruby 匹配第一次出现的字符串以进行 gsub 替换

Ruby - 获取正在执行的文件

如何使 Sinatra 通过 HTTPS/SSL 工作?

在一组字符串中找到最长的公共起始子字符串

无法在 Heroku 教程中使用 Python 启动工头

Ruby 中的 Fail vs. raise:我们真的应该相信风格指南吗?

jekyll 调试或打印所有变量

使用正则表达式在第一个逗号之前提取文本

如何找到安装 Ruby Gem 的路径(即 Gem.lib_path c.f. Gem.bin_path)

如何让 Ruby 解析时间,就好像它在不同的时区一样?

将 Ruby 哈希转换为 YAML

如何获取从位置 N 到 Ruby 中最后一个字符的子字符串?

如何获取当前 rake 任务的 PID?

如何将哈希键转换为方法名称?

使用哈希值呈现 ERB 模板

Integer 和 Fixnum 有什么区别?

何时在 Ruby 中使用 Struct 而不是 Hash?