Rubocop 25行ブロックサイズとRSpecのテスト
質問
典型的な RSpec ユニットテストでは、コードを構造化し、DSL "magic" を使用して spec を BDD ステートメントのように読ませるために、入れ子の Ruby ブロックを広範に使用します。
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
理想的な仕様では、各例は比較的短くて正確なものになります。しかし、RSpecの構造がこのように機能しているため、外側のブロックが100行以上になることはよくあることで、多くのspec例が必要ではなく、それぞれが数行の具体的な設定を持っていて
describe
ブロックの大きさは、説明する対象のコードと同じかそれ以上になります。
最近の Rubocop のアップグレードで、ブロックの長さは 25 行以内という新しいルールが導入されました。に記載されていないので、その根拠はよくわかりません。
Ruby スタイルガイド
. 良いことだと思い、デフォルトのルールセットに追加するのはわかるのですが。しかし、アップグレード後、Rubocop のテストが何度も失敗し、次のようなメッセージが表示されるようになりました。
tests/component_spec.rb:151:3: C: Block has too many lines. [68/25]
Rubocop のようなコードメトリックツールを使って、私は のように のポリシーを持っています (主に、タブとスペースやその他の些細なことを議論するのは時間の無駄であり、IME の場合 は決して ここでは、明らかに不可能です。2 つのコア データ品質ツールが、コード レイアウト アプローチについて意見が一致しません。
これに対して、私たちは単に Rubocop ブロック サイズ ルールを高いしきい値に設定しました。しかし、そうすると、何が足りないのだろう?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
. . . つまり、それは可能なのですが、このように大量の spec 例をヘルパー関数にすることは、RSpec の設計で推奨されている読みやすいアプローチとは正反対のようです。
それを無視する方法を見つける以外に何かできることはありますか?
このトピックについて、最も近い既存の質問をここで見つけることができました。 RSpec & Rubocop / Ruby スタイルガイド で、これはテストテンプレートを編集することで解決できるように見えました。
どうやって解決するの?
最近の Rubocop のアップグレードで、ブロックの長さを 25 行以下にするという新しいルールが導入されました。Rubyのスタイルガイドには記載されていないので、その根拠はよくわかりません。
かつては、すべての警官は The Ruby Style Guide に基づいており、RuboCop はコミュニティによって定められた慣習を遵守するための方法でした。
その後方向性が変わり、RuboCop の範囲は、開発者がコードベース全般の一貫性を確保するのを助けるために広がりました。これは、2 つのことをもたらしました。
- コップ (Ruby スタイル ガイドに基づくものであっても) はほとんど設定可能になりました。
- Ruby スタイルガイドに記載されていないものでも、プロジェクトで一貫性を持たせるために有用なコップがあります。
この警官は2番目のカテゴリに属します。
RSpecはコードレイアウトのために今では信用されていないアプローチを使用していますか?また、RSpecのテストでブロックサイズを減らすために、どのような合理的なオプションがあるでしょうか?
短い答えは「いいえ」です。DSL はまだイケてます :-)
この警官は命令型プログラミングの意味での大きなブロックを対象としています。一般的なガイドとして、それはしばしば宣言的であるDSLには適用されません。例えば、長い
routes.rb
というファイルがあるのは、まったく問題ありません。これはスタイル違反というよりも、大規模なアプリケーションの自然な結果です。(そして、多くのテストを持つことは純粋に素晴らしいことです)。
さて、RuboCop はかなり賢いのですが、何が DSL で何がそうでないかを知らないので、自動的に無視することはできません。Rails routes や RSpec spec など、人気のあるフレームワークの DSL 入力メソッドを除外することは可能だという意見もあるでしょう。そうしない理由は主に
- 偽陰性。どのクラスも同じ名前のブロックを取って、メソッドを実装することができます。
-
RuboCopはRubyの解析ツールであり、外部ライブラリについては本当に知らないはずです。(ただし
/spec
ディレクトリを除外するのは、適切な拡張システムができるまでの礼儀であり、これを処理するにはrubocop-rspec
gem で処理できます)。
つまり、それは可能なのですが、この方法でspecのサンプルを大量にヘルパー関数にすることは、RSpecの設計が推奨する読みやすいアプローチとは正反対のようです。
肝心なのは RuboCop は、私たちがよりよいコードを書くのを助けるためにあります。もし私たちのアプリケーションの設計が健全であるにもかかわらず、RuboCop を喜ばせるためだけに可読性を下げているのであれば、フィルタリングするか、設定するか、あるいは無効にすべきです :-)
これに対して、私たちは単に Rubocop のブロック サイズ規則を高いしきい値に設定しました。しかし、これでは、何を見逃しているのでしょうか?
これはかなり鈍感なツールで、あなたがほのめかしているように、おそらくそのために偽陰性が発生するでしょう。この警官の偽陽性には 2 つのタイプがあります。
- 純粋に宣言的な DSL を含むファイル (例: Rails ルート、RSpec 仕様)。
-
宣言的なDSLが、ほとんど命令的なコードに混じっているファイル、例えば
aasm
ステートマシン宣言のようなものです。
最初のケースでは、ファイルまたはディレクトリを除外することが最適な解決策であり、2番目のケースでは、インライン無効化を使用することです。
あなたの場合は
.rubocop.yml
を使ってください。
Metrics/BlockLength:
Exclude:
- 'Rakefile'
- '**/*.rake'
- 'test/**/*.rb'
(リストが上書きされるため、デフォルトの設定から基本的な除外を再度行う必要があることに注意してください)。
関連
-
[解決済み] レスキューVSシドキック?[クローズド]
-
[解決済み] Rubyで空のファイルを作成する:"touch "と同等?
-
[解決済み] Sinatraがファイルを変更するたびに自動で再読み込みするようにするには?
-
[解決済み] 今月の名前(Date.today.monthをnameに変換)。
-
[解決済み] Rubyのプライベートモジュールメソッド
-
[解決済み] ruby の rescue 節に複数のエラークラスを DRY に渡す方法
-
[解決済み] Ruby で改行せずに印刷する方法
-
[解決済み] ランダムなブール値を返す最良の方法
-
[解決済み] Mavericks と Xcode 5.1 で Ruby Gem install Json が失敗する - unknown argument: '-multiply_definedsuppress'.
-
[解決済み] Rubyのモジュール/ミキシンからクラスメソッドを継承する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Rubyでコンソールから入力を読み込む?
-
[解決済み] Rubyでモジュール変数を作成する
-
[解決済み] 今月の名前(Date.today.monthをnameに変換)。
-
[解決済み] RubyのREPLを開くコマンドは何ですか?
-
[解決済み] Hash.new([]などのHashのデフォルト値を使用すると、予期せぬ動作(値が消える/変わる)が発生します。)
-
[解決済み] Rubyで文字列が正規表現にマッチするかどうかをチェックする最も速い方法?
-
[解決済み] Ruby 配列を関数の引数に変換する
-
[解決済み] Ruby の正規表現で最初にマッチしたものを返す
-
[解決済み] Rubyで1行でメソッドを定義するには?
-
[解決済み] Rubyでディレクトリを再帰的にリストアップするためのワンライナー?