RSpec MocksのREADMEを翻訳

# 翻訳する目的

[Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)を読み、モック関連と、FactoryBotについて更に知識をつけたいと思いました。どちらも技術本で学ぶのは難しいので、リポジトリのREADMEを通じて学ぶのが良いと考えました。
Google翻訳やDeepLを用いて全文翻訳を行うと、日本語が不自然で読みにくかったので、今後何度も参照することを考えて、自分で翻訳することにしました。
FactoryBotのREADMEの翻訳は既に存在していたので、[RSpec MocksのREADME](https://github.com/rspec/rspec-mocks/blob/main/README.md)を翻訳することにしました。

# 翻訳時のルール

- Google翻訳やDeepLは利用しない。
- わからない単語を辞書で調べるのはOK。

# 翻訳してみてどうだったか？

良い点

- 英語のドキュメントを読む練習になる。
- ドキュメントを細部までしっかり読む経験ができる。わからなくても投げ出さない経験ができる。
- 他の人が参照しても参考になる可能性が高い。

悪い点

- 時間がかかる。（本記事で5~6時間程度）

ここから下が翻訳した内容です。

# RSpec Mocks

rspec-mocksはRSpec向けのテストダブルのフレームワークであり、テストダブルや本物のオブジェクトにおけるメソッドのスタブやフェイク、message expectationをサポートしています。

## インストール

```
gem install rspec       # rspec-core, rspec-expectations, rspec-mocks
gem install rspec-mocks # rspec-mocksだけ
```

`main`ブランチに対して実行しますか？依存するRSpecのリポジトリも含める必要があります。次のように`Gemfile`に追加してください。

```ruby
%w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
  gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main'
end
```

## コントリビューティング

環境をセットアップした後には、作業を行いたいリポジトリのディレクトリに移動する必要があります。移動先において、スペックやCucmberのテストを実行したり、パッチを作成することができます。

NOTE: 任意のRSpecリポジトリで作業する際、rspec-devを利用する必要はありません。それぞれのRSpecリポジトリを独立したプロジェクトとして扱うことができます。

RSpecへのコントリビューションについての情報は、下記のmarkdownファイルを参照してください。

- [ビルドについての詳細](https://github.com/rspec/rspec-mocks/blob/main/BUILD_DETAIL.md)
- [行動規範](https://github.com/rspec/rspec-mocks/blob/main/CODE_OF_CONDUCT.md)
- [コントリビューティングガイドについての詳細](https://github.com/rspec/rspec-mocks/blob/main/CONTRIBUTING.md)
- [開発のためのセットアップガイド](https://github.com/rspec/rspec-mocks/blob/main/DEVELOPMENT.md)

## テストダブル

テストダブルは、RSpecのexampleにおいて皆さんのシステムの全く別のオブジェクトの代役となるオブジェクトです。テストダブルを作成するには`double`メソッドを利用してください。引数には識別子をオプションとして渡します。

```ruby
book = double("book")
```

大抵の場合、作成したダブルがシステム上に既に存在するオブジェクトとそっくりであるかが気になるはずです。そのために、検証付きダブルが提供されています。もし既存のオブジェクトが利用可能な状態であれば、既存のオブジェクトには存在しない、もしくは引数の数が不適切なメソッドを追加することはできません。

```ruby
book = instance_double("Book", :pages => 250)
```

検証付きダブルには巧妙なトリックが利用されています。本物のオブジェクトと同じようにバリデーションを行える一方で、依存関係とは隔離された環境でテストを行うことができます。詳細は[ドキュメント](https://github.com/rspec/rspec-mocks/blob/main/features/verifying_doubles)で確認できます。

検証付きダブルもdouble()の場合と同様に、カスタム識別子を渡すことができます。下記が例です。

```ruby
books = []
books << instance_double("Book", :rspec_book, :pages => 250)
books << instance_double("Book", "(Untitled)", :pages => 5000)

puts books.inspect # 名前が付けられていれば、どれが追加されたのかがわかりやすいです。
```

## メソッドのスタブ

メソッドのスタブとは、予め決められた値を返却することです。スタブはテストダブルに対しても、本物のオブジェクトに対しても同じ文法を用いて定義することができます。rspec-mocksはスタブを定義するための3つの方法をサポートしています。

```ruby
allow(book).to receive(:title) { "The RSpec Book" }
allow(book).to receive(:title).and_return("The RSpec Book")
allow(book).to receive_messages(
    :title => "The RSpec Book",
    :subtitle => "Behaviour-Driven Development with RSpec, Cucumber, and Friends")
```

また、下記のようなショートカットを利用することもできます。1つの宣言でダブルを作成しつつ、スタブを定義しています。

```ruby
book = double("book", :title => "The RSpec Book")
```

第一引数は名前であり、ドキュメンテーションやテスト失敗時のメッセージに利用されます。もし、名前付けが不要である場合は、それを取り除いて、インスタンスとスタブの定義を簡潔にすることができます。

```ruby
double(:foo => 'bar')
```

この書き方は、ダブルのリストを生成し、そのリストをイテレートさせるメソッドに渡したい場合に特に有効です。

```ruby
order.calculate_total_price(double(:price => 1.99), double(:price => 2.99))
```

### メソッドチェーンをスタブする

`receive`を利用していた箇所で`receive_message_chain`を使うと、メッセージのチェーンをスタブすることができます。

```ruby
allow(double).to receive_message_chain("foo.bar") { :baz }
allow(double).to receive_message_chain(:foo, :bar => :baz)
allow(double).to receive_message_chain(:foo, :bar) { :baz }

# 上記のいずれかの形式の場合
double.foo.bar # => :baz
```

チェーンは任意の長さにすることができるため、デメテルの法則に違反しやすいです。そのため、コードスメルとなる`receive_message_chain`の利用には注意が必要です。fluent interfacesをイメージしてください。全てのコードスメルが問題の存在を指し示す訳ではありませんが、それでも`receive_message_chain`は脆いテストケースの原因となってしまうでしょう。例えば、スペック内で`allow(foo).to receive_message_chain(:bar, :baz => 37)`と記述し、実行時に`foo.baz.bar`を呼び出すと、このスタブは動作しません。

## 連続する戻り値

スタブが1回以上呼び出される場合、`and_return`にさらに引数を渡すことができます。呼び出される度にリストを循環し、さらに後続の呼び出しに対しては最後の値を返し続けます。

```ruby
allow(die).to receive(:roll).and_return(1, 2, 3)
die.roll # => 1
die.roll # => 2
die.roll # => 3
die.roll # => 3
die.roll # => 3
```

一度の呼び出しに対して配列を返したい場合は、配列を定義してください。

```ruby
allow(team).to receive(:players).and_return([double(:name => "David")])
```

## Message Expectations

message expectationはexampleが終了するまでの間にダブルがメッセージを受け取ることをテストします。メッセージを受ければテストは成功であり、受け取らなければテストは失敗です。

```ruby
validator = double("validator")
expect(validator).to receive(:validate) { "02134" }
zipcode = Zipcode.new("02134", validator)
zipcode.valid?
```

## テストスパイ

オブジェクトがテストを実行する過程で期待しているメッセージを受けとることを検証します。メッセージを検証するためには、オブジェクトがスパイになるために明示的にスタブされる、もしくはnullオブジェクトのダブル(e.g. `double(...).as_null_object`)になる必要があります。そのために、nullオブジェクトのダブルを簡単に作成するメソッドが提供されています。

```ruby
spy("invitation") # => `double("invitation").as_null_object`と同じ
instance_spy("Invitation") # => `instance_double("Invitation").as_null_object`と同じ
class_spy("Invitation") # => `class_double("Invitation").as_null_object`と同じ
object_spy("Invitation") # => `object_double("Invitation").as_null_object`と同じ
```

この方法で受け取ったメッセージを検証すると、テストスパイのパターンを実装することができます。

```ruby
invitation = spy('invitation')

user.accept_invitation(invitation)

expect(invitation).to have_received(:accept)

# 他の一般的なメッセージエクスペクテーションも利用できます。下記が例です。
expect(invitation).to have_received(:accept).with(mailer)
expect(invitation).to have_received(:accept).twice
expect(invitation).to_not have_received(:accept).with(mailer)

# ダブルの場合と同じように戻り値を指定することができます。
invitation = spy('invitation', :accept => true)
expect(invitation).to have_received(:accept).with(mailer)
expect(invitation.accept).to eq(true)
```

スパイにメッセージが渡された後に、その引数が変更された場合、`have_received(...).with(...)`は正しく動作しないことに注意してください。例えば、下記は正しく動作しません。

```ruby
greeter = spy("greeter")

message = "Hello"
greeter.greet_with(message)
message << ", World"

expect(greeter).to have_received(:greet_with).with("Hello")
```

## 命名法

### モックオブジェクトとテストスタブ

モックオブジェクトとテストスタブという名前は特別なテストダブルを表します。例えば、テストスタブはメソッドのスタブのみを扱うテストダブルであり、モックオブジェクトはmessage expectationとメソッドのスタブの両方を扱うテストダブルです。

命名法には重複するものがたくさんあり、その重複パターン(フェイクやスパイなど)にも多くのバリエーションがあります。大抵の場合、メソッドのスタブやmessage expectationsのバリエーションについてのメソッドレベルの概念について扱っていること、そしてそれらを汎用オブジェクトであるテストダブルに当てはめていることを覚えておいてください。

### Test-Specific Extension

Test-Specific Extensionはテストのコンテキストにおいてテストダブルのような振る舞いとなるように実装された、本物のオブジェクトの拡張のことであり、別名、Partial Doubleと呼ばれます。クラスオブジェクトがメソッドのグローバル名前空間として機能することがよくあるため、この手法はRubyではとても一般的です。

```ruby
person = double("person")
allow(Person).to receive(:find) { person }
```

このケースでは、Personが`find`メッセージを受け取ると定義したpersonオブジェクトを必ず返すように実装しています。また、もし`find`が呼び出されなかった場合にexampleが失敗するようにmessage expectationを設定することもできます。

```ruby
person = double("person")
expect(Person).to receive(:find) { person }
```

RSpecはスタブ、もしくはモックしたメソッドをtest-double-likeなメソッドに置き換えます。exampleの最後には、RSpecはmessage expectationを検証し、その後に元のメソッドを復元します。

## 引数を期待する

```ruby
expect(double).to receive(:msg).with(*args)
expect(double).to_not receive(:msg).with(*args)
```

また、同じmessageに対して複数のexpectationを設定することも可能です。

```ruby
expect(double).to receive(:msg).with("A", 1, 3)
expect(double).to receive(:msg).with("B", 2, 4)
```

## 引数のマッチャー

`with`に渡された引数は実際に渡された引数と===によって比較されます。もし、引数そのものよりも、引数に関するモノを指定したい場合、 rspec-expectationsに含まれるマッチャーを利用することができます。それらは全てが文法的に意味をなすわけではありません（それらはRSpec::Expectationsで使用することを第一に設計されています）。しかし、独自のRSpec::Matchersを自由に作成することができます。

```ruby
expect(double).to receive(:msg).with(no_args)
expect(double).to receive(:msg).with(any_args)
expect(double).to receive(:msg).with(1, any_args) # any argsはsplat演算子を利用した引数のように振る舞う
expect(double).to receive(:msg).with(1, kind_of(Numeric), "b") # 第2引数はNumericクラスのあらゆる値になりうる
expect(double).to receive(:msg).with(1, boolean(), "b") # 第2引数はtrueもしくはfalseになりうる2nd argument can be true or false
expect(double).to receive(:msg).with(1, /abc/, "b") # 第2引数は正規表現にマッチするStringクラスのあらゆる値になりうる
expect(double).to receive(:msg).with(1, anything(), "b") # 第2引数はどんな値にもなりうる
expect(double).to receive(:msg).with(1, duck_type(:abs, :div), "b") # 第2引数はabsとdivに応答するオブジェクトになりうる
expect(double).to receive(:msg).with(hash_including(:a => 5)) # 第1引数はkey-valuesの1つがa: 5であるハッシュ
expect(double).to receive(:msg).with(array_including(5)) # 第1引数はkey-valuesの1つが5である配列
expect(double).to receive(:msg).with(hash_excluding(:a => 5)) # 第1引数はkey-valuesにa: 5を含まないハッシュ
```

## 受け取る回数

```ruby
expect(double).to receive(:msg).once
expect(double).to receive(:msg).twice
expect(double).to receive(:msg).exactly(n).time
expect(double).to receive(:msg).exactly(n).times
expect(double).to receive(:msg).at_least(:once)
expect(double).to receive(:msg).at_least(:twice)
expect(double).to receive(:msg).at_least(n).time
expect(double).to receive(:msg).at_least(n).times
expect(double).to receive(:msg).at_most(:once)
expect(double).to receive(:msg).at_most(:twice)
expect(double).to receive(:msg).at_most(n).time
expect(double).to receive(:msg).at_most(n).times
```

## 順番

```ruby
expect(double).to receive(:msg).ordered
expect(double).to receive(:other_msg).ordered
  # messageを順番通りに受け取らないと、これは失敗します。
```

これは異なる引数の同じmessageを含めることもできます。

```ruby
expect(double).to receive(:msg).with("A", 1, 3).ordered
expect(double).to receive(:msg).with("B", 2, 4).ordered
```

## レスポンスを設定する

message expectationもしくはスタブを設定する場合、オブジェクトにどのようにレスポンスすべきかを正確に伝えることができます。最も汎用的な方法は`receive`にブロックを渡すことです。

```ruby
expect(double).to receive(:msg) { value }
```

ダブルが`msg`というmessageを受け取った場合、ブロックが評価され、結果が返されます。

```ruby
expect(double).to receive(:msg).and_return(value)
expect(double).to receive(:msg).exactly(3).times.and_return(value1, value2, value3)
  # value1を最初に返し、value2を2番目に返す、など
expect(double).to receive(:msg).and_raise(error)
  # `error`はインスタンスオブジェクト(e.g. `StandardError.new(some_arg)`)、もしくはクラス(e.g. `StandardError`)になりうる
  # クラスの場合は、引数なしでインスタンス化可能でないといけない
expect(double).to receive(:msg).and_throw(:msg)
expect(double).to receive(:msg).and_yield(values, to, yield)
expect(double).to receive(:msg).and_yield(values, to, yield).and_yield(some, other, values, this, time)
  # ブロックに対して複数回yieldされるメソッドに対して
```

これらのレスポンスはスタブに対しても同様に適用できます。

```ruby
allow(double).to receive(:msg).and_return(value)
allow(double).to receive(:msg).and_return(value1, value2, value3)
allow(double).to receive(:msg).and_raise(error)
allow(double).to receive(:msg).and_throw(:msg)
allow(double).to receive(:msg).and_yield(values, to, yield)
allow(double).to receive(:msg).and_yield(values, to, yield).and_yield(some, other, values, this, time)
```

## 恣意的に扱う

解決したい問題を利用可能なexpectationで解決できないこともあると思います。特定の長さの配列が含まれるmessageを期待しているが、その配列の内容については意識しないことをイメージしてください。下記のようにすることができます。

```ruby
expect(double).to receive(:msg) do |arg|
  expect(arg.size).to eq 7
end
```

もし、スタブされたメソッドがブロックを受け取る場合、特別な方法でyieldする必要があります。下記のようにできます。

```ruby
expect(double).to receive(:msg) do |&arg|
  begin
    arg.call
  ensure
    # cleanup
  end
end
```

## オリジナルの実行に移譲する

部分的なモックオブジェクトを利用する場合、メッセージに対してどのように応答するかに干渉することなくmessage expectationを設定したいことがあると思います。これを実現するために`and_call_original`を利用することができます。

```ruby
expect(Person).to receive(:find).and_call_original
Person.find # => オリジナルのfindメソッドを実行し、結果を返す
```

## expectationの詳細を連結する

message名を特定の引数、受け取る回数、レスポンスと連結させることで、expectationにおいて詳細を得ることができます。

```ruby
expect(double).to receive(:<<).with("illegal value").once.and_raise(ArgumentError)
```

これは必要な場面では良いですが、おそらく実際には必要ないでしょう！あなたのコードの振る舞いにおいて重要なことだけを指定するように気をつけましょう。

## 定数をスタブし、隠す

この機能の情報は[可変定数のREADME](https://github.com/rspec/rspec-mocks/blob/main/features/mutating_constants/README.md)を確認してください。

## `before(:context)`ではなく、`before(:example)`を利用してください

`before(:context)`内におけるスタブはサポートされていません。なぜなら、全てのスタブやモックはそれぞれのexampleの後に消去されるので、`before(:context)`で設定されたスタブはグループの中で偶然実行された最初のexampleで機能し、他のexampleでは機能しないからです。

`before(:context)`の代わりに、`before(:example)`を利用してください。

## あるクラスのどんなインスタンスにもモック、もしくはスタブを設定する

rspec-mocksはあるクラスのどんなインスタンスでもスタブ、モックするために`allow_any_instance_of`と`expect_any_instance_of`という2つのメソッドを提供しています。それらは`allow`や`expect`の位置で利用されます。

```ruby
allow_any_instance_of(Widget).to receive(:name).and_return("Wibble")
expect_any_instance_of(Widget).to receive(:name).and_return("Wobble")
```

これらのメソッドは`Widget`の全てのインスタンスに適切なスタブ、もしくはexpectationを追加します。

この機能はレガシーコードでは便利に機能することもありますが、一般的にはいくつかの理由で利用することを推奨しません。

- `rspec-mocks`のAPIは個別のインスタンスオブジェクト向けに設計されています。しかし、この機能はオブジェクトのクラス全体を扱います。結果として、意味的に混乱させるようなエッジケースが存在しています。例えば、`expect_any_instance_of(Widget).to receive(:name).twice`において、特定のインスタンスがそれぞれ`name`を2回受け取ることを期待しているのか、それとも合計で2回受け取ることを期待しているのかがわかりにくいです。（この場合は前者です）
- この機能を利用は、しばしばデザインのスメルになります。それは、テストが過度に多くのことをテストしようとしているか、テスト対象のオブジェクトが過度に複雑である可能性があるということです。
- これは`rspec-mocks`において最も複雑な機能であり、歴史的に最もバグ報告を受け取っています。（コアチームも誰も、この役に立たない機能を積極的に利用していません）

## 参考文献

モックやスタブの意味について、様々な異なる観点があります。もし、更に学ぶことに興味があるのであれば、下記がオススメの記事です。

- Mock Objects: http://www.mockobjects.com/
- Endo-Testing: http://www.ccs.neu.edu/research/demeter/related-work/extreme-programming/MockObjectsFinal.PDF
- Mock Roles, Not Objects: http://www.jmock.org/oopsla2004.pdf
- Test Double: http://www.martinfowler.com/bliki/TestDouble.html
- Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html
- Mocks aren't stubs: http://www.martinfowler.com/articles/mocksArentStubs.html

## こちらもご確認ください

- https://github.com/rspec/rspec
- https://github.com/rspec/rspec-core
- https://github.com/rspec/rspec-expectations
- https://github.com/rspec/rspec-rails