この記事はDeepLの結果を編集したものです。修正点などはお気軽に編集リクエストをしてください。
RubyはShopifyのメイン言語です。私たちは主にRubyを使用しており、おそらく最大規模のショップのひとつです。Rubyは新しいWebプロジェクトやスクリプティングのための言語です。
Shopifyではすべての開発者が少なくともRubyを一通り理解していることを期待しています。素晴らしい言語です。日々どのような仕事をしていても、より良い開発者になれるでしょう。以下に示すのはRubyでの開発中に従うべき緩やかなコーディングスタイルです。
このスタイルガイドはShopifyでの10年以上に渡るRuby開発の成果です。内容の多くはBozhidar BatsovのRubyスタイルガイドに基づいており、多くのコントリビューターによってShopifyに適応されています。
RuboCopで使う
本スタイルガイドをRubyプロジェクトで採用する際には、RuboCopを利用することを推奨します。RuboCopのインストール方法や使い方については、RuboCopの公式ドキュメントを参照してください。
デフォルトのRuboCopの設定を継承することで、このスタイルガイドと同期することができます。これを利用するにはGemfile
に以下を追加します:
gem "rubocop-shopify", require: false
そしてプロジェクトのRuboCop設定ファイルの先頭に以下を追加します:
inherit_gem:
rubocop-shopify: rubocop.yml
指定されたInclude
やExclude
の設定はRuboCopのデフォルトにマージされます。
gemからの設定の継承の詳細については、RuboCopのドキュメントを参照してください。
一般
- メソッドのすべての行が同じ抽象化レベルで動作するようにする。(単一抽象レベルの原則)。
- 機能的にコーディングする。できる限り変異(副作用)を避ける。
- 防御的なプログラミングを避ける
過度に防御的なプログラミングは、決して遭遇することのないエラーに対して安全策を講じる可能性があり、その結果としてランタイムとメンテナンスのコストが発生する。
- 引数の変異を避ける。
- モンキーパッチは避ける。
- 長いメソッドは避ける。
- 長いパラメータリストは避ける。
- 不必要なメタプログラミングを避ける。
-
private
とprotected
の可視性を阻害しないように、send
よりもpublic_send
を優先する。 -
ruby -w
で安全なコードを書く。 - 3レベル以上のブロックネストを避ける。
レイアウト
- ソースファイルのエンコーディングには
UTF-8
を使用する。 - 2スペースのインデント、タブなし。
- Unixスタイルの改行コードを使用する。
- ステートメントや式の区切りに
;
を使わない。1行に1つの式を使用する。 - 演算子の周り、カンマ、コロン、セミコロンの後ろ、
{
の周り、}
の前にはスペースを使う。 -
(
,[
の後、]
,)
の前にはスペースを入れない。 -
!
演算子の後にはスペースを入れない。 - 範囲リテラル内にはスペースを入れない。
- メソッド呼び出し演算子の周囲はスペースを入れない。
# bad
foo . bar
# good
foo.bar
- ラムダリテラル内のスペースを避ける。
# bad
a = -> (x, y) { x + y }
# good
a = ->(x, y) { x + y }
-
case
の行と同じ深さまでwhen
をインデントする。 - 条件式の結果を変数に代入する場合は、その分岐を戻り値を受け取る変数に合わせる。
# bad
result =
if some_cond
# ...
# ...
calc_something
else
calc_something_else
end
# good
result = if some_cond
# ...
# ...
calc_something
else
calc_something_else
end
-
begin
ブロックの結果を代入する場合は、rescue/ensure/end
を行頭に揃える。
# bad
host = begin
URI.parse(value).host
rescue URI::Error
nil
end
# good
host = begin
URI.parse(value).host
rescue URI::Error
nil
end
- メソッド定義とメソッドの間には空行を使い、内部的にはメソッドを論理的な段落に分割する。
- メソッドのパラメータにデフォルト値を代入する場合は、
=
演算子の周囲に空白文字を使用する。 - 必要でない場合は
\
で行を継続することを避ける。 - メソッド呼び出しのパラメータが複数行にまたがる場合は、メソッド呼び出しのある行の開始位置に対して1レベル字下げして揃える。
# starting point (line is too long)
def send_mail(source)
Mailer.deliver(to: "bob@example.com", from: "us@example.com", subject: "Important message", body: source.text)
end
# bad (double indent)
def send_mail(source)
Mailer.deliver(
to: "bob@example.com",
from: "us@example.com",
subject: "Important message",
body: source.text)
end
# good
def send_mail(source)
Mailer.deliver(
to: "bob@example.com",
from: "us@example.com",
subject: "Important message",
body: source.text,
)
end
複数の行でメソッドを連結する場合は、連続する呼び出しを1レベルずつインデントする。
# bad (indented to the previous call)
User.pluck(:name)
.sort(&:casecmp)
.chunk { |n| n[0] }
# good
User
.pluck(:name)
.sort(&:casecmp)
.chunk { |n| n[0] }
- 複数行にまたがる配列リテラルの要素を揃える。
- 行は120文字以内にする。
- 末尾の空白は避ける。
- アラインメント目的以外の余分な空白は避ける。
- 各ファイルの最後は改行する。
- ブロックコメントは避ける。
# bad
=begin
comment line
another comment line
=end
# good
# comment line
# another comment line
- 括弧が最初の引数とは別の行にある場合は、メソッド呼び出しの括弧を最後の引数の後の行に置く。
# bad
method(
arg_1,
arg_2)
# good
method(
arg_1,
arg_2,
)
- メソッド呼び出し、ハッシュ、配列を複数行でラップする場合は、各要素/引数を改行する。
# bad
method(arg_1, arg_2,
arg_3
)
[
value_1, value_2,
value_3,
]
{
key1: value_1,
key2: value_2, key3: value_3,
}
# good
method(
arg_1,
arg_2,
arg_3,
)
[
value_1,
value_2,
value_3,
]
{
key1: value_1,
key2: value_2,
key3: value_3,
}
# good (special cases)
# Single argument method call
method({
foo: bar,
})
# Last argument, itself is multiline
class User
after_save :method, if: -> {
do_some_checks
}
end
# Single value array
errors = [{
error_code: 1234,
error_message: "This is an error",
}]
- マジックコメントとコードやドキュメントは空行で区切る。
# good
# frozen_string_literal: true
# Some documentation for Person
class Person
# Some code
end
# bad
# frozen_string_literal: true
# Some documentation for Person
class Person
# Some code
end
- 属性アクセサーの周囲に空行を使う。
# bad
class Foo
attr_reader :foo
def foo
# do something...
end
end
# good
class Foo
attr_reader :foo
def foo
# do something...
end
end
- メソッド、クラス、モジュール、ブロック本体の周囲で空行は避ける。
# bad
class Foo
def foo
begin
do_something do
something
end
rescue
something
end
true
end
end
# good
class Foo
def foo
begin
do_something do
something
end
rescue
something
end
end
end
シンタックス
- 定数(クラスやモジュールも含む)やコンストラクタ(
Array()
やNokogiri::HTML()
など)を参照する場合にのみ::
を使用する。通常のメソッド呼び出しには::
を避ける。 - 定数参照は親クラス/モジュール内を検索しないため、クラスやモジュールの定義、継承には
::
を使うことを避ける。
# bad
module A
FOO = "test"
end
class A::B
puts FOO # this will raise a NameError exception
end
# good
module A
FOO = "test"
class B
puts FOO
end
end
- パラメータがある場合は
def
に括弧をつける。メソッドがパラメータを受け付けない場合は括弧を省略する。 -
for
は避ける。 -
then
は避ける。 -
if/then/else/end
構文よりも三項演算子(?:
)を使う。
# bad
result = if some_condition then something else something_else end
# good
result = some_condition ? something : something_else
- 三項演算子では1つの分岐につき1つの式を使用する。これは三項演算子を入れ子にしてはいけないということでもある。このような場合は
if/else
構文を優先する。 - 複数行の
?:
(三項演算子)は避け、代わりにif/unless
を使用する。 - 1行の場合は
when x then ...
を使う。 -
not
の代わりに!
を使う。 -
and
/or
より&&
/||
を優先する。 - 否定条件の場合は
if
よりunless
を優先する。 -
else
を使ったunless
は避ける。ポジティブな条件から書き直す。 - メソッド呼び出しの引数を括弧で囲む。引数を指定しない場合は括弧を省略する。また、呼び出しが単一行でメソッドが以下の場合は括弧を省略する:
- 暗黙のレシーバを持つクラスメソッド呼び出し。
- 糖衣構文による呼び出し(例:
1 + 1
は+
メソッドを呼び出し、foo[bar]
は[]
メソッドを呼び出すなど)。
# bad
class User
include(Bar)
has_many(:posts)
end
# good
class User
include Bar
has_many :posts
SomeClass.some_method(:foo)
end
-
- 以下のいずれかのメソッド:
require
require_relative
require_dependency
yield
raise
puts
- 以下のいずれかのメソッド:
-
暗黙のオプションハッシュを囲む中括弧は省略する。
-
呼び出されたメソッドがブロックの唯一の操作である場合、proc呼び出しの省略記法を使う。
# bad
names.map { |name| name.upcase }
# good
names.map(&:upcase)
- 1行ブロックの場合は
do...end
よりも{...}
を優先する。 - 複数行のブロックでは
{...}
よりもdo...end
を優先する。 -
return
は可能であれば省略する。 -
self
は可能であれば省略する。
# bad
self.my_method
# good
my_method
# also good
attr_writer :name
def my_method
self.name = "Rafael" # `self` is needed to reference the attribute writer.
end
- 条件文の中で返り値を使用する場合は代入を括弧で囲む。
if (value = /foo/.match(string))
- 変数の初期化には、まだ初期化されていない場合にのみ
||=
を使う。 - 真偽値の初期化に
||=
を使うのは避ける。
# bad - would set enabled to true even if it was false
@enabled ||= true
# good
@enabled = true if @enabled.nil?
# also valid - defined? workaround
@enabled = true unless defined?(@enabled)
- メソッド名と開始括弧の間にスペースを入れることを避ける。
-
lambda
よりもlambdaリテラル構文を優先する。
# bad
l = lambda { |a, b| a + b }
l.call(1, 2)
l = lambda do |a, b|
tmp = a * 7
tmp * b / 50
end
# good
l = ->(a, b) { a + b }
l.call(1, 2)
l = ->(a, b) do
tmp = a * 7
tmp * b / 50
end
-
Proc.new
よりもproc
を優先する。 - 使用しないブロックパラメーターの先頭には
_
を付ける。_
だけでも構わない。 - 無効なデータを保証できる場合はガード節を優先する。ガード節とは関数の先頭にある条件文のことで、できる限り早く抜け出す。
# bad
def compute_thing(thing)
if thing[:foo]
update_with_bar(thing)
if thing[:foo][:bar]
partial_compute(thing)
else
re_compute(thing)
end
end
end
# good
def compute_thing(thing)
return unless thing[:foo]
update_with_bar(thing[:foo])
return re_compute(thing) unless thing[:foo][:bar]
partial_compute(thing)
end
- オプションハッシュよりもキーワード引数を優先する。
-
collect
よりmap
、detect
よりfind
、find_all
よりselect
、length
よりsize
を優先する。 -
DateTime
よりもTime
を優先する。 -
"2018-03-20T11:16:39-04:00"
のようなISO8601形式の時間文字列を期待する場合、Time.parse(foo)
ではなくTime.iso8601(foo)
を優先する。 - 代入コンテキストで
begin
ブロックから戻るのは避ける。begin
ブロック内のメソッドからリターンすると、代入が行われなくなり、混乱を招くメモ化バグが発生する可能性がある。
# bad
def foo
@foo ||= begin
return 1 if flag?
2
end
end
# good
def foo
@foo ||= begin
if flag?
1
else
2
end
end
end
名前付け
- シンボル、メソッド、変数には
snake_case
を使う。 - クラスとモジュールには
CamelCase
を使うが、HTTP、RFC、XMLのような頭字語は大文字にする。 -
hello_world.rb
のようにファイルやディレクトリの名前にはsnake_case
を使う。 - 1つのソースファイルに1つのクラスまたはモジュールを定義する。ファイル名はクラス名やモジュール名と同じにするが、
CamelCase
をsnake_case
に置き換える。 - その他の定数には
SCREAMING_SNAKE_CASE
を使う。 - 短いブロックで
inject
を使用する場合、注入されるものに応じて引数に名前を付ける。例:|hash, e|
(mnemonic: hash, element) - 二項演算子を定義する場合、引数の名前を
other
にする(<<
と[]
はセマンティクスが異なるので、このルールの例外となる)。 - 述語メソッドには
?
を付ける。述語メソッドは真偽値を返すメソッドである。 - ブール値を返さない場合はメソッド名の最後に
?
を付けないようにする。 - メソッド名の先頭に
is_
を付けない。
# bad
def is_empty?
end
# good
def empty?
end
- メソッド名を
get_
で始めるのは避ける。 - 破壊的変更を伴わない同等のメソッドがない場合、メソッド名の最後を
!
で終わらせないようにする。例えばActiveRecordではsave
はブール値を返すが、save!
は失敗時に例外をスローする。 - マジックナンバーは避ける。定数を使い、意味のある名前をつける。
- 差別的な由来を持つ(と解釈される)命名法は避ける。
コメント
- 読者が見逃しているかもしれないので、関連する文脈をコメントに含める。
- コメントはコードと同期させる。
- 適切な大文字と句読点を使ってコメントを書く。
- 余計なコメントは避ける。コードがどのように動作するかではなく、コードがなぜそのようになっているかに焦点を当てる。
クラスとモジュール
- クラスメソッドだけを持つクラスよりもモジュールを優先する。クラスはそこからインスタンスを生成することに意味がある場合にのみ使うべきである。
-
module_function
よりもextend self
を優先する。
# bad
module SomeModule
module_function
def some_method
end
def some_other_method
end
end
# good
module SomeModule
extend self
def some_method
end
def some_other_method
end
end
- クラスメソッドを定義するときは
def self
よりもclass << self
のブロックを使い、1つのブロックにまとめる。
# bad
class SomeClass
def self.method1
end
def method2
end
private
def method3
end
def self.method4 # this is actually not private
end
end
# good
class SomeClass
class << self
def method1
end
private
def method4
end
end
def method2
end
private
def method3
end
end
- クラス階層を設計するときはリスコフの置換原則を守る。
-
attr_accessor
、attr_reader
、attr_writer
を使用して、些細なアクセサとミューテーターを定義する。
# bad
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def first_name
@first_name
end
def last_name
@last_name
end
end
# good
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
end
-
attr
よりもattr_reader
やattr_accessor
を優先する。 - クラス変数(
@@
)は避ける。 -
public
、protected
、private
メソッドを、それらが適用されるメソッド定義と同じようにインデントする。visibility修飾子の上に1行、下に1行空行を置く。
class SomeClass
def public_method
# ...
end
private
def private_method
# ...
end
def another_private_method
# ...
end
end
-
alias
よりもalias_method
を優先する。
例外
-
raise
メソッドを使って例外を通知する。 - 引数2つのバージョンの
raise
ではRuntimeError
を省略する。
# bad
raise RuntimeError, "message"
# good - signals a RuntimeError by default
raise "message"
- 例外インスタンスではなく例外クラスとメッセージを2つの別々の引数として与えて
raise
することを優先する。
# bad
raise SomeException.new("message")
# Note that there is no way to do `raise SomeException.new("message"), backtrace`.
# good
raise SomeException, "message"
# Consistent with `raise SomeException, "message", backtrace`.
-
ensure
ブロックからのリターンは避ける。ensure
ブロック内のメソッドから明示的にリターンすると、発生した例外よりもリターンが優先され、メソッドは例外がまったく発生しなかったかのように戻る。事実上、例外は静かに捨てられる。
# bad
def foo
raise
ensure
return "very bad idea"
end
- 可能な限り暗黙の
begin
ブロックを使用する。
# bad
def foo
begin
# main logic goes here
rescue
# failure handling goes here
end
end
# good
def foo
# main logic goes here
rescue
# failure handling goes here
end
- 空の
rescue
構文は避ける。
# bad
begin
# an exception occurs here
rescue SomeError
# the rescue clause does absolutely nothing
end
# bad - `rescue nil` swallows all errors, including syntax errors, and
# makes them hard to track down.
do_something rescue nil
-
rescue
の修飾形は避ける。
# bad - this catches exceptions of StandardError class and its descendant
# classes.
read_file rescue handle_error($!)
# good - this catches only the exceptions of Errno::ENOENT class and its
# descendant classes.
def foo
read_file
rescue Errno::ENOENT => error
handle_error(error)
end
-
Exception
クラスのレスキューは避ける。
# bad
begin
# calls to exit and kill signals will be caught (except kill -9)
exit
rescue Exception
puts "you didn't really want to exit, right?"
# exception handling
end
# good
begin
# a blind rescue rescues from StandardError, not Exception.
rescue => error
# exception handling
end
- 新しい例外クラスを導入するよりも標準ライブラリの例外を優先する。
- 例外変数には意味のある名前を使う。
# bad
begin
# an exception occurs here
rescue => e
# exception handling
end
# good
begin
# an exception occurs here
rescue => error
# exception handling
end
コレクション
- コンストラクタにパラメータを渡す必要がない限り、リテラル配列とハッシュ生成記法を使用する。
# bad
arr = Array.new
hash = Hash.new
# good
arr = []
hash = {}
-
%w
や%i
よりもリテラル配列構文を優先する。
# bad
STATES = %w(draft open closed)
# good
STATES = ["draft", "open", "closed"]
- 複数行のコレクションリテラルの末尾にカンマを追加する。
# bad
{
foo: :bar,
baz: :toto
}
# good
{
foo: :bar,
baz: :toto,
}
- 配列の最初か最後の要素にアクセスするときは、
[0]
や[-1]
よりもfirst
やlast
を優先する。 - 変更可能なオブジェクトをハッシュのキーにしない。
- すべてのキーがシンボルである場合は、省略記法のハッシュリテラル構文を使用する。
# bad
{ :a => 1, :b => 2 }
# good
{ a: 1, b: 2 }
- すべてのキーが記号でない場合は、省略記法よりもハッシュロケット構文を優先する。
# bad
{ a: 1, "b" => 2 }
# good
{ :a => 1, "b" => 2 }
-
Hash#has_key?
よりもHash#key?
を優先する。 -
Hash#has_value?
よりもHash#value?
を優先する。 - 存在すべきハッシュキーを扱う場合は
Hash#fetch
を使う。
heroes = { batman: "Bruce Wayne", superman: "Clark Kent" }
# bad - if we make a mistake we might not spot it right away
heroes[:batman] # => "Bruce Wayne"
heroes[:supermann] # => nil
# good - fetch raises a KeyError making the problem obvious
heroes.fetch(:supermann)
- カスタムロジックを使用するのではなく、
Hash#fetch
を使用してハッシュキーのデフォルト値を導入する。
batman = { name: "Bruce Wayne", is_evil: false }
# bad - if we just use || operator with falsy value we won't get the expected result
batman[:is_evil] || true # => true
# good - fetch work correctly with falsy values
batman.fetch(:is_evil, true) # => false
- 中括弧が最初の要素とは別の行にある場合は、最後の要素の後の行に ] と } を置く。
# bad
[
1,
2]
{
a: 1,
b: 2}
# good
[
1,
2,
]
{
a: 1,
b: 2,
}
文字列
- 文字列連結の代わりに文字列補間と文字列書式を優先する:
# bad
email_with_name = user.name + " <" + user.email + ">"
# good
email_with_name = "#{user.name} <#{user.email}>"
# good
email_with_name = format("%s <%s>", user.name, user.email)
- 補間式では括弧内のパディングスペースは避ける。
# bad
"From: #{ user.first_name }, #{ user.last_name }"
# good
"From: #{user.first_name}, #{user.last_name}"
- 文字列は二重引用符で囲む。
# bad
'Just some text'
'No special chars or interpolation'
# good
"Just some text"
"No special chars or interpolation"
"Every string in #{project} uses double_quotes"
- 文字リテラル構文
?x
は避ける。 - 文字列に補間されるインスタンス変数やグローバル変数の周囲には
{}
を使用する。
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
# bad - valid, but awkward
def to_s
"#@first_name #@last_name"
end
# good
def to_s
"#{@first_name} #{@last_name}"
end
end
$global = 0
# bad
puts "$global = #$global"
# fine, but don't use globals
puts "$global = #{$global}"
- 補間されたオブジェクトの
Object#to_s
を避ける。
# bad
message = "This is the #{result.to_s}."
# good - `result.to_s` is called implicitly.
message = "This is the #{result}."
- より高速で専門的な代替手段を使うことができる場面では
String#gsub
を避ける。
url = "http://example.com"
str = "lisp-case-rules"
# bad
url.gsub("http://", "https://")
str.gsub("-", "_")
str.gsub(/[aeiou]/, "")
# good
url.sub("http://", "https://")
str.tr("-", "_")
str.delete("aeiou")
- 複数行の文字列にheredocsを使用する場合、先頭の空白を保持するという事実に注意する。余分な空白をカットするために、ある程度のマージンを取ることは良い習慣となる。
code = <<-END.gsub(/^\s+\|/, "")
|def test
| some_method
| other_method
|end
END
# => "def test\n some_method\n other_method\nend\n"
# In Rails you can use `#strip_heredoc` to achieve the same result
code = <<-END.strip_heredoc
def test
some_method
other_method
end
END
# => "def test\n some_method\n other_method\nend\n"
- Ruby 2.3ではRailsの
strip_heredoc
と同じセマンティクスを持つ"squiggly heredoc"
構文を優先する:
code = <<~END
def test
some_method
other_method
end
END
# => "def test\n some_method\n other_method\nend\n"
- heredocのコンテンツとクロージングを、その開始に従ってインデントする。
# bad
class Foo
def bar
<<~SQL
'Hi'
SQL
end
end
# good
class Foo
def bar
<<~SQL
'Hi'
SQL
end
end
# bad
# heredoc contents is before closing heredoc.
foo arg,
<<~EOS
Hi
EOS
# good
foo arg,
<<~EOS
Hi
EOS
# good
foo arg,
<<~EOS
Hi
EOS
正規表現
- 文字列の正規表現よりもプレーンテキスト検索を優先する。
string["text"]
- キャプチャした結果を使用しない場合は、キャプチャしないグループを使用する。
# bad
/(first|second)/
# good
/(?:first|second)/
- Perl-legacy変数よりも
Regexp#match
を優先してグループマッチを捕捉する。
# bad
/(regexp)/ =~ string
process $1
# good
/(regexp)/.match(string)[1]
- 番号付きグループよりも名前付きグループを優先する。
# bad
/(regexp)/ =~ string
...
process Regexp.last_match(1)
# good
/(?<meaningful_var>regexp)/ =~ string
...
process meaningful_var
- 文字列の先頭から末尾までをマッチングする場合、
^
と$
よりも\A
と\z
を優先する。
string = "some injection\nusername"
string[/^username$/] # `^` and `$` matches start and end of lines.
string[/\Ausername\z/] # `\A` and `\z` matches start and end of strings.
パーセントリテラル
- 補間と埋め込みダブルクォートの両方が必要な1行文字列には
%()
を使用する。複数行の文字列にはheredocsを使う。 -
'
と"
の両方を含む文字列でない限り%q
は避ける。正規文字列リテラルの方が読みやすいので、多くの文字をエスケープする必要がない限り、そちらを使うべきである。 -
%r
は少なくとも1つの/
文字にマッチする正規表現にのみ使う。
# bad
%r{\s+}
# good
%r{^/(.*)$}
%r{^/blog/2011/(.*)$}
-
%s
の使用は避ける。空白を含む記号を作成するには:"some string"
を使用する。 - 正規表現でよくあるように、括弧がリテラル内に現れる場合を除き、すべての
%
リテラルの区切り文字として()
を使用する。()
、{}
、[]
、<>
のうち、リテラル内に現れない最初のものを使用する。
テスト
- テストコードも他のコードと同じように扱うこと。つまり、可読性、保守性、複雑さなどを念頭に置く。
- テストフレームワークとしてMinitestを使用する。
- 各テストケースはコードのひとつの側面をカバーするものに限定する。
- テストケースのセットアップ、アクション、アサーションの各セクションを空行で区切った段落にまとめる。
test "sending a password reset email clears the password hash and set a reset token" do
user = User.create!(email: "bob@example.com")
user.mark_as_verified
user.send_password_reset_email
assert_nil user.password_hash
refute_nil user.reset_token
end
- 複雑なテストケースを機能を分離してテストする複数の単純なテストに分割する。
- テストケースを定義するのに
def test_foo
よりもtest "foo"
スタイルの構文を優先する。 - より説明的なエラーメッセージを出力するアサーションメソッドを優先する。
# bad
assert user.valid?
assert user.name == "tobi"
# good
assert_predicate user, :valid?
assert_equal "tobi", user.name
-
assert_nothing_raised
の使用は避ける。代わりに肯定的なアサーションを使う。 - 期待よりもアサーションを優先する。期待は特にシングルトンオブジェクトとの組み合わせにおいて、より脆いテストにつながる。
# bad
StatsD.expects(:increment).with("metric")
do_something
# good
assert_statsd_increment("metric") do
do_something
end