minitestでモック、スタブする（RR、WebMock、MiniTest::Mockを使う）

# この記事について
仕事で、railsのテストフレームワークにminitestを使っています。そして、railsプロジェクトの中では、モックやスタブをするときに、RR、WebMockといったgemや、minitest標準のモックであるMiniTest::Mockが使われています。
テストを書くときに、モックやスタブの書き方に戸惑うことが多くありました。
この記事では、テストダブルとはどんなものか、モックとスタブの違いはなにか、RR、WebMock、MiniTest::Mockそれぞれの使い方について記述します。

# 目次
- [テストダブル（モック、スタブ）とは](https://qiita.com/nobu09/items/bda6205a8f170a1887d7#%E3%83%86%E3%82%B9%E3%83%88%E3%83%80%E3%83%96%E3%83%AB%E3%83%A2%E3%83%83%E3%82%AF%E3%82%B9%E3%82%BF%E3%83%96%E3%81%A8%E3%81%AF)
- [RR](https://qiita.com/nobu09/items/bda6205a8f170a1887d7#rr)
    - Rubyのテストダブルのフレームワークのgem
- [WebMock](https://qiita.com/nobu09/items/bda6205a8f170a1887d7#webmock)
    - RubyでHTTPリクエストのスタブやモックを設定するためのgem
- [MiniTest::Mock](https://qiita.com/nobu09/items/bda6205a8f170a1887d7#minitestmock)
    - minitestに含まれるモックオブジェクトのフレームワーク
- [最後に](https://qiita.com/nobu09/items/bda6205a8f170a1887d7#%E6%9C%80%E5%BE%8C%E3%81%AB)
- [参考情報](https://qiita.com/nobu09/items/bda6205a8f170a1887d7#%E5%8F%82%E8%80%83%E6%83%85%E5%A0%B1)

# テストダブル（モック、スタブ）とは
まず、テストダブルとはどんなものでしょう。また、モック、スタブの違いはなんでしょう。

- テストダブルとは、ソフトウェアテストにおいて、テスト対象が依存しているコンポーネントを置き換える代用品（ダブルは代役、影武者を意味する）
- モックもスタブも、テストダブルの一種

## テストダブルの5つのバリエーション
テストダブルは、[xUnit Test Pattern](http://xunitpatterns.com)の書籍によると、5つのバリエーションがあります。
私は、スタブとモックをよく混同していましたが、**スタブは依存するコンポーネントを置き換えるものであり、モックはテスト対象コードからの出力が期待通りであるか検証するものである**、ということを理解しました。

1. テストスタブ
    - テスト対象コードが依存する実際のコンポーネントを置き換えるために使用する
    - テスト時の呼び出しときに、あらかじめ決められた値を返すように設定する

2. テストスパイ
    - テスト対象コードが実行された時の間接的な出力をキャプチャし、後のテストによる検証のために保存する
    - 呼び出しに基づく情報を記録するスタブ

3. モックオブジェクト
    - テスト対象コードが実行される際に、テスト対象コードからの間接的な出力を検証するために使用するオブジェクト
    - 間接出力の検証に重きが置かれる
    - 期待した呼び出しが行われたかを検証できる（どんな引数で呼ばれたか、など）

4. フェイクオブジェクト
    - テスト対象コードの依存コンポーネントを置き換えるオブジェクト
    - 依存コンポーネントと同じ機能を実装しているが、よりシンプルな方法で実装されている
    - フェイクを利用する一般的な理由は、実際の依存コンポーネントがまだ利用できない、遅すぎる、または副作用があるためにテスト環境で使用できない、などがある

5. ダミーオブジェクト
    - テスト対象コードのメソッドシグネチャの中に、パラメータとしてオブジェクトを必要とする場合に、ダミーオブジェクトを使う（テストもテスト対象コードでもこのオブジェクトを気に掛けていない場合）

参考
[Test Double / xUnit Patterns.com](http://xunitpatterns.com/Test%20Double.html)
[wiki テストダブル](https://ja.wikipedia.org/wiki/%E3%83%86%E3%82%B9%E3%83%88%E3%83%80%E3%83%96%E3%83%AB)
[自動テストのスタブ・スパイ・モックの違い](https://gotohayato.com/content/483/)

# RR
RRは、Rubyのテストダブルのフレームワークのgemです。
読み方は、'Double Ruby'と読むそうです。
RRにはアダプタが用意されているので、RSpec、Test::Unit、MiniTest/MiniSpecなどのテストフレームワークと統合することができるようです。

GitHub：https://github.com/rr/rr
公式：http://rr.github.io/rr/
> RR is a test double framework that features a rich selection of double techniques and a terse syntax.
（訳：RRは、豊富なダブルテクニックと簡潔な構文を特徴とするテストダブルフレームワークです。）

## RRの使い方
RRには、モック、スタブ、プロキシ、スパイが実装されています。
[RRのGitHubページ](https://github.com/rr/rr#a-whirlwind-tour-of-rr)のサンプル通りですが、こんな感じで書けます。

### スタブ
`stub`で、スタブする（実際の呼び出しを置き換える）ことができます。

```ruby
# 何も返さないメソッドをスタブする
stub(object).foo
stub(MyClass).foo

# 常に値を返すスタブメソッド
stub(object).foo { 'bar' }
stub(MyClass).foo { 'bar' }

# 特定の引数で呼び出されたときに値を返すスタブメソッド
stub(object).foo(1, 2) { 'bar' }
stub(MyClass).foo(1, 2) { 'bar' }
```

詳細は[stub](https://github.com/rr/rr/blob/master/doc/03_api_overview.md#stub)のページ参照。

### モック
`mock`で、期待した呼び出しが行われるか検証するモックを作成できます。

```ruby
# メソッドが呼ばれることを期待する
# objectのfooメソッドが呼ばれることを期待する
mock(object).foo
mock(MyClass).foo

# メソッドに期待値を作成し、常に指定した値を返すようにスタブする
# objectのfooメソッドが'bar'を返すことを期待する
mock(object).foo { 'bar' }
mock(MyClass).foo { 'bar' }

# 特定の引数を持つメソッドに期待値を作成し、それを返すためにスタブを作成する
# objectのfooメソッドが引数1, 2で呼ばれ、'bar'を返すことを期待する
mock(object).foo(1, 2) { 'bar' }
mock(MyClass).foo(1, 2) { 'bar' }
```

詳細は[mock](https://github.com/rr/rr/blob/master/doc/03_api_overview.md#mock)ページ参照。

### スパイ
`stub`と、`assert_received`や`expect(xxx).to have_received`の記述を組み合わせて、スパイ（呼び出された情報を記録するスタブ）が書けるようです。
（公式GitHubには、Test::UnitとRspecでの書き方はありましたが、minitestでの書き方は載っていませんでした。）

```ruby
# RSpec
stub(object).foo
expect(object).to have_received.foo

# Test::Unit
stub(object).foo
assert_received(object) {|o| o.foo }
```

### プロキシ
`proxy`を使うと、メソッドを完全にオーバーライドせずにインターセプトして新しい戻り値を設定したスタブやモックが作れるようです。

```ruby
# 既存のメソッドを完全にオーバーライドせずにインターセプトして
# 既存の値から新しい戻り値を取得する
stub.proxy(object).foo {|str| str.upcase }
stub.proxy(MyClass).foo {|str| str.upcase }

# 上記の例でやってることに加えて、さらに期待値のモックを作成する
mock.proxy(object).foo {|str| str.upcase }
mock.proxy(MyClass).foo {|str| str.upcase }

# クラスの新しいメソッドをインターセプトし、戻り値にダブルを定義する
stub.proxy(MyClass).new {|obj| stub(obj).foo; obj }

# 上記の例でやってることに加えて、.newに期待値のモックを作成する
mock.proxy(MyClass).new {|obj| stub(obj).foo; obj }
```

詳細は[mock.proxy](https://github.com/rr/rr/blob/master/doc/03_api_overview.md#mockproxy)、[stub.proxy](https://github.com/rr/rr/blob/master/doc/03_api_overview.md#stubproxy)ページ参照。

### クラスのインスタンス
`any_instance_of`で、インスタンス作成時にメソッドをスタブしたりモックできます。また、`stub.proxy`を使うと、インスタンスそのものにアクセスできるようになります。

```ruby
# MyClass のインスタンスの作成時にメソッドをスタブする
any_instance_of(MyClass) do |klass|
  stub(klass).foo { 'bar' }
end

# インスタンス自体にアクセスできるようにする別の方法
# MyClass.newされたインスタンスobjをスタブしている
stub.proxy(MyClass).new do |obj|
  stub(obj).foo { 'bar' }
end
```

詳細は[#any_instance_of](https://github.com/rr/rr/blob/master/doc/03_api_overview.md#any_instance_of)ページ参照。

### [Pureなモックオブジェクト](https://github.com/rr/rr/blob/master/doc/02_syntax_comparison.md#pure-mock-objects)
モックのためだけにオブジェクトを使用したい場合は、空のオブジェクトを作成することで可能です。

```ruby
mock(my_mock_object = Object.new).hello
```

ショートカットとして`mock!`を使うこともできます。

```ruby
# 空の #hello メソッドを持つ新しいモックオブジェクトを作成し、そのモックを取得する
# モックオブジェクトを #subject メソッドで取得できる
my_mock_object = mock!.hello.subject
```

### [#dont_allow](https://github.com/rr/rr/blob/master/doc/03_api_overview.md#dont_allow-aliased-to-do_not_allow-dont_call-and-do_not_call)
`#dont_allow`は`#mock`の逆で、ダブルには絶対にコールされないという期待を設定します。ダブルが実際に呼び出された場合、`TimesCalledError`が発生します。

```ruby
dont_allow(User).find('42')
User.find('42') # raises a TimesCalledError
```

### その他
RRは`#method_missing`を使ってメソッドの期待値を設定しているそうです。これにより、`#should_receive`や`#expects`メソッドを使う必要がありません。
また、引数の期待値を設定するために`#with`メソッドを使う必要がないそうです。（使いたければ使えます）

```ruby
mock(my_object).hello('bob', 'jane')
mock(my_object).hello.with('bob', 'jane')  # withがついているが上と同じ
```

RRは、ブロックを使って戻り値を設定することをサポートしています。(お好みで、`#returns`を使うことができます)

```ruby
mock(my_object).hello('bob', 'jane') { 'Hello Bob and Jane' }
mock(my_object).hello('bob', 'jane').returns('Hello Bob and Jane')  # returnsがついているが上と同じ
```

`#times`、`#at_least`、`#at_most`、`#any_times`メソッドでモックの期待する呼び出し回数を調整できます。`#with_any_args`でどんな引数での呼び出しも許容したり、`#with_no_args`で引数なしの呼び出しを期待したり、`#never`でメソッドが呼ばれないことを期待したりできます。
もっと詳しい情報は、[API overview](https://github.com/rr/rr/blob/master/doc/03_api_overview.md)を参照ください。

# WebMock
WebMockは、RubyでHTTPリクエストのスタブやモックを設定するためのgemです。
RRとの違いは、HTTPリクエストに特化している部分でしょうか。

GitHub：https://github.com/bblimke/webmock

> Library for stubbing and setting expectations on HTTP requests in Ruby.

[機能](https://github.com/bblimke/webmock#features)として、以下を提供しています。

- HTTP リクエストを低レベルの http クライアントの lib レベルでスタブ化 (HTTP ライブラリを変更する際にテストを変更する必要はない)
- HTTP リクエストに対する期待値の設定と検証
- メソッド、URI、ヘッダ、ボディに基づいたリクエストのマッチング
- 異なる表現における同じ URI のスマートマッチング (エンコードされた形式と非エンコードされた形式)
- 異なる表現での同じヘッダのスマートなマッチング
- Test::Unit、RSpec、minitest のサポート

## WebMockの使い方
[WebMockのGitHubページ](https://github.com/bblimke/webmock#examples)のサンプルから、使い方を抜粋します。


### スタブ
`stub_request`でリクエストをスタブすることができます。

#### [uri のみに基づくスタブ付きリクエストとデフォルトのレスポンス](https://github.com/bblimke/webmock#stubbed-request-based-on-uri-only-and-with-the-default-response)

```ruby
stub_request(:any, "www.example.com")    # スタブ（anyを使う）
Net::HTTP.get("www.example.com", "/")    # ===> Success
```

#### [メソッド、URI、ボディ、ヘッダに基づいたスタブリクエスト](https://github.com/bblimke/webmock#stubbing-requests-based-on-method-uri-body-and-headers)

```ruby
# スタブ
stub_request(:post, "www.example.com").
  with(body: "abc", headers: { 'Content-Length' => 3 })

uri = URI.parse("http://www.example.com/")
req = Net::HTTP::Post.new(uri.path)
req['Content-Length'] = 3
res = Net::HTTP.start(uri.host, uri.port) do |http|
  http.request(req, "abc")
end    # ===> Success
```

#### [リクエストボディをハッシュと照合](https://github.com/bblimke/webmock#matching-request-body-against-a-hash-body-can-be-url-encoded-json-or-xml)
ボディが、URL-Encode、JSON、XML のいずれかのとき、リクエストボディをハッシュと照合できます。

```ruby
# スタブ
stub_request(:post, "www.example.com").
  with(body: {data: {a: '1', b: 'five'}})

RestClient.post('www.example.com', "data[a]=1&data[b]=five",
  content_type: 'application/x-www-form-urlencoded')    # ===> Success
RestClient.post('www.example.com', '{"data":{"a":"1","b":"five"}}',
  content_type: 'application/json')    # ===> Success
RestClient.post('www.example.com', '<data a="1" b="five" />',
  content_type: 'application/xml')    # ===> Success
```

`hash_including`を使うと、部分的なハッシュとリクエストボディを照合できます。

```ruby
# bodyをhash_includingで部分的なハッシュで照合
# bodyが全て一致していなくても照合できる
stub_request(:post, "www.example.com").
  with(body: hash_including({data: {a: '1', b: 'five'}}))

RestClient.post('www.example.com', "data[a]=1&data[b]=five&x=1",
:content_type => 'application/x-www-form-urlencoded')    # ===> Success
```

#### [クエリパラメータの照合](https://github.com/bblimke/webmock#matching-query-params-using-hash)

ハッシュでクエリパラメータを照合できます。

```ruby
# スタブ
stub_request(:get, "www.example.com").with(query: {"a" => ["b", "c"]})

RestClient.get("http://www.example.com/?a[]=b&a[]=c")    # ===> Success
```

ボディと同様、`hash_including`で部分ハッシュとクエリパラメータと照合できます。

```ruby
stub_request(:get, "www.example.com").
  with(query: hash_including({"a" => ["b", "c"]}))

RestClient.get("http://www.example.com/?a[]=b&a[]=c&x=1")    # ===> Success
```

`hash_excluding`を使うと、クエリパラメータ に含まれていない状態に照合できます。

```ruby
stub_request(:get, "www.example.com").
  with(query: hash_excluding({"a" => "b"}))

RestClient.get("http://www.example.com/?a=b")    # ===> Failure
RestClient.get("http://www.example.com/?a=c")    # ===> Success
```

#### [カスタムレスポンスを返すスタブ](https://github.com/bblimke/webmock#stubbing-with-custom-response)

`to_return`でカスタムレスポンスを返すスタブを設定できます。

```ruby
# スタブ
stub_request(:any, "www.example.com").
  to_return(body: "abc", status: 200,
    headers: { 'Content-Length' => 3 })

Net::HTTP.get("www.example.com", '/')    # ===> "abc"
```

#### [エラーをraiseする](https://github.com/bblimke/webmock#raising-errors)

```ruby
# クラスで宣言された例外のraise
stub_request(:any, 'www.example.net').to_raise(StandardError)
RestClient.post('www.example.net', 'abc')    # ===> StandardError

# 例外インスタンスのraise
stub_request(:any, 'www.example.net').to_raise(StandardError.new("some error"))

# 例外メッセージで例外をraise
stub_request(:any, 'www.example.net').to_raise("some error")
```

`to_timeout`で、[タイムアウト例外](https://github.com/bblimke/webmock#raising-timeout-errors)のraiseもできます。

```ruby
stub_request(:any, 'www.example.net').to_timeout

RestClient.post('www.example.net', 'abc')    # ===> RestClient::RequestTimeout
```

#### [繰り返すリクエストに複数の異なるレスポンス](https://github.com/bblimke/webmock#multiple-responses-for-repeated-requests)

リクエストが繰り返された時、複数の異なるレスポンスを返すことができます。
また、[`to_return`、`to_raise`、`to_timeout`を`then`でつないで複数のレスポンスを返したり](https://github.com/bblimke/webmock#multiple-responses-using-chained-to_return-to_raise-or-to_timeout-declarations)、[`times`を使ってレスポンスを返す回数と指定する](https://github.com/bblimke/webmock#specifying-number-of-times-given-response-should-be-returned)こともできます。

```ruby
stub_request(:get, "www.example.com").
  to_return({body: "abc"}, {body: "def"})
Net::HTTP.get('www.example.com', '/')    # ===> "abc\n"
Net::HTTP.get('www.example.com', '/')    # ===> "def\n"

# すべてのレスポンスが使用された後、最後のレスポンスが無限に返される
Net::HTTP.get('www.example.com', '/')    # ===> "def\n"
```

#### [ネットワークへのリアルリクエストを許可または無効化](https://github.com/bblimke/webmock#real-requests-to-network-can-be-allowed-or-disabled)

`WebMock.allow_net_connect!`で、実際のネットワークへのリクエストを許可できます。`WebMock.disable_net_connect!`で無効化することもできます。
[特定のリクエストを許可しながら、外部リクエストを無効にする](https://github.com/bblimke/webmock#external-requests-can-be-disabled-while-allowing-specific-requests)こともできます。

```ruby
# 実際のネットワークへのリクエストを許可
WebMock.allow_net_connect!
stub_request(:any, "www.example.com").to_return(body: "abc")

Net::HTTP.get('www.example.com', '/')    # ===> "abc"
Net::HTTP.get('www.something.com', '/')    # ===> /.+Something.+/

# 実際のネットワークへのリクエストを無効化
WebMock.disable_net_connect!

Net::HTTP.get('www.something.com', '/')    # ===> Failure
```

他にも、様々な方法で、スタブすることができます。他の使い方のサンプルコードは[Stubbing](https://github.com/bblimke/webmock#stubbing)ページを参照ください。

### 期待値の設定（モック）
WebMockのGitHubページには、[Test::Unit](https://github.com/bblimke/webmock#setting-expectations-in-testunit)、[RSpecでの期待値の設定方法](https://github.com/bblimke/webmock#setting-expectations-in-rspec-on-webmock-module)の記述はありましたが、minitestについて記述がありませんでした。
minitestは、Test::Unitと同様の書き方ができそうです（[参考](https://stackoverflow.com/questions/37263232/how-to-test-external-request-with-ministest-testcase-out-of-controller-scope)）。

#### [Test::Unit/minitest](https://github.com/bblimke/webmock#setting-expectations-in-testunit)

`assert_requested`や`assert_not_requested`を使います。

```ruby
require 'webmock/test_unit'

stub_request(:any, "www.example.com")

uri = URI.parse('http://www.example.com/')
req = Net::HTTP::Post.new(uri.path)
req['Content-Length'] = 3
res = Net::HTTP.start(uri.host, uri.port) do |http|
  http.request(req, 'abc')
end

assert_requested :post, "http://www.example.com",
  headers: {'Content-Length' => 3}, body: "abc",
  times: 1    # ===> Success

assert_not_requested :get, "http://www.something.com"    # ===> Success

assert_requested(:post, "http://www.example.com",
  times: 1) { |req| req.body == "abc" }
```

スタブを使って期待値を設定するためには、以下のように書きます。

```ruby
stub_get = stub_request(:get, "www.example.com")
stub_post = stub_request(:post, "www.example.com")

Net::HTTP.get('www.example.com', '/')

assert_requested(stub_get)
assert_not_requested(stub_post)
```

#### Rspec
`expect`と`have_requested`を組み合わせて書きます。

```ruby
require 'webmock/rspec'

expect(WebMock).to have_requested(:get, "www.example.com").
  with(body: "abc", headers: {'Content-Length' => 3}).twice

expect(WebMock).not_to have_requested(:get, "www.something.com")

expect(WebMock).to have_requested(:post, "www.example.com").
  with { |req| req.body == "abc" }
# Note that the block with `do ... end` instead of curly brackets won't work!
# Why? See this comment https://github.com/bblimke/webmock/issues/174#issuecomment-34908908

expect(WebMock).to have_requested(:get, "www.example.com").
  with(query: {"a" => ["b", "c"]})

expect(WebMock).to have_requested(:get, "www.example.com").
  with(query: hash_including({"a" => ["b", "c"]}))

expect(WebMock).to have_requested(:get, "www.example.com").
  with(body: {"a" => ["b", "c"]},
    headers: {'Content-Type' => 'application/json'})
```

`a_request`と`have_been_made`を組み合わせて以下のようにも書けます。

```ruby
expect(a_request(:post, "www.example.com").
  with(body: "abc", headers: {'Content-Length' => 3})).
  to have_been_made.once

expect(a_request(:post, "www.something.com")).to have_been_made.times(3)

expect(a_request(:post, "www.something.com")).to have_been_made.at_least_once

expect(a_request(:post, "www.something.com")).
  to have_been_made.at_least_times(3)

expect(a_request(:post, "www.something.com")).to have_been_made.at_most_twice

expect(a_request(:post, "www.something.com")).to have_been_made.at_most_times(3)

expect(a_request(:any, "www.example.com")).not_to have_been_made

expect(a_request(:post, "www.example.com").with { |req| req.body == "abc" }).
  to have_been_made

expect(a_request(:get, "www.example.com").with(query: {"a" => ["b", "c"]})).
  to have_been_made

expect(a_request(:get, "www.example.com").
  with(query: hash_including({"a" => ["b", "c"]}))).to have_been_made

expect(a_request(:post, "www.example.com").
  with(body: {"a" => ["b", "c"]},
    headers: {'Content-Type' => 'application/json'})).to have_been_made
```

スタブを使って期待値を設定するためには、以下のように書きます。

```ruby
stub = stub_request(:get, "www.example.com")
# ... make requests ...
expect(stub).to have_been_requested
```

詳細は[期待値の設定](https://github.com/bblimke/webmock#setting-expectations)ページを参照ください。

### その他
[`WebMock.reset!`](https://github.com/bblimke/webmock#clearing-stubs-and-request-history)で現在のスタブとリクエストの履歴をすべてリセットしたり、[`WebMock.reset_executed_requests!`](https://github.com/bblimke/webmock#clearing-stubs-and-request-history)で実行されたリクエストのカウンタのみをリセットできます。
[`WebMock.disable!`や`WebMock.enable!`](https://github.com/bblimke/webmock#disabling-and-enabling-webmock-or-only-some-http-client-adapters)で、WebMock を無効にしたり有効にしたり、一部の http クライアントアダプタのみを有効にすることができます。
他の機能については、[WebMockのGitHubページのサンプルコード](https://github.com/bblimke/webmock#examples)を参照ください。

# MiniTest::Mock
最後に、MiniTest::Mockは、minitestに含まれているモックオブジェクトのフレームワークです。

公式ドキュメント：http://docs.seattlerb.org/minitest/Minitest/Mock.html

> A simple and clean mock object framework.
All mock objects are an instance of Mock.
（シンプルでクリーンなモックオブジェクトフレームワークです。すべてのモックオブジェクトは MiniTest::Mockのインスタンスです。）

## MiniTest::Mockの使い方
### スタブ
オブジェクトをスタブする[`stub`](http://docs.seattlerb.org/minitest/Object.html#method-i-stub)は、Minitest::Mock のオブジェクト拡張です。
スタブが有効なのはブロック内のみで、ブロックの最後にスタブはクリーンアップされます。また、スタブする前にメソッド名が存在している必要があります。
`stub_any_instance`メソッドは、クラスのインスタンス上にメソッドスタブを作成できます。[minitest-stub_any_instance_of](https://github.com/codeodor/minitest-stub_any_instance)のgemを導入すると使うことができます。



- stub：オブジェクトのメソッドをスタブする
- stub_any_instance_of：クラスのインスタンスメソッドをスタブする

`stub`のサンプルコードです。

```ruby
require 'minitest/autorun'

# スタブする対象のクラス
class Hello
  def say
    'Hello!'
  end
end

hello = Hello.new
# helloオブジェクトのsayメソッドが'Hello, this is from stub!'を返すようにスタブする
hello.stub(:say, 'Hello, this is from stub!') do
  hello.say  #==> "Hello, this is from stub!"
end
# ブロックを抜けるとスタブは無効になる
hello.say  #==> "Hello!"
```

`stub_any_instance`を使うと、インスタンスメソッドのスタブを以下のように書けます。インスタンスメソッドのスタブを書くときはこちらの方が使える場面が多そうです。

```ruby
require 'minitest/autorun'
require 'minitest/stub_any_instance'  # minitest-stub_any_instance_ofのgemも必要

# スタブする対象のクラス
class Hello
  def say
    'Hello!'
  end
end

# Helloクラスの任意のインスタンスのsayメソッドが'Hello, this is from stub!'を返すようにスタブする
Hello.stub_any_instance(:say, 'Hello, this is from stub!') do
  Hello.new.say  #==> "Hello, this is from stub!"
end
# ブロックを抜けるとスタブは無効になる
Hello.new.say  #==> "Hello!"
```

### モック
#### expectメソッド
`expect(name, retval, args = [], &blk)
メソッド名(name)が呼ばれ、オプションで引数(args)またはブロック(blk)を指定し、戻り値(retval)を返すことを期待します。

```ruby
require 'minitest/autorun'

@mock.expect(:meaning_of_life, 42)
@mock.meaning_of_life # => 42

@mock.expect(:do_something_with, true, [some_obj, true])
@mock.do_something_with(some_obj, true) # => true

@mock.expect(:do_something_else, true) do |a1, a2|
  a1 == "buggs" && a2 == :bunny
end
```

引数は、'==='演算子を使って期待される引数と比較されるので、より具体的な期待値が少なくて済むようになっています。（含まれるか？で比較される）

```ruby
require 'minitest/autorun'

# users_any_stringメソッドがStringに含まれる場合、trueを返す
@mock.expect(:uses_any_string, true, [String])
@mock.uses_any_string("foo") # => true
@mock.verify  # => true（期待通りにモックが呼ばれたのでtrueになる）

@mock.expect(:uses_one_string, true, ["foo"])
@mock.uses_one_string("bar") # => raises MockExpectationError（期待通りにモックが呼ばれなかったため）
```

メソッドが複数回呼ばれる場合は、それぞれに新しい期待値を指定します。これらは定義した順番で使用されます。

```ruby
require 'minitest/autorun'

@mock.expect(:ordinal_increment, 'first')
@mock.expect(:ordinal_increment, 'second')

@mock.ordinal_increment # => 'first'
@mock.ordinal_increment # => 'second'
@mock.ordinal_increment # => raises MockExpectationError "No more expects available for :ordinal_increment"
```

#### verifyメソッド
すべてのメソッドが期待通りに呼び出されたことを確認します。期待通りに呼ばれたら`true`を返します。モックオブジェクトが期待通りに呼ばれなかった場合、`MockExpectationError`を発生させます。

詳しくは、[MiniTest::Mock](http://docs.seattlerb.org/minitest/Minitest/Mock.html)ページを参照ください。

# 最後に
RRもWebMockも、公式ドキュメントに十分な使い方のサンプルが掲載されていたので、一読してみると良さそうです。MiniTest::Mockの情報量は少なめだったので、`irb`や`rails c`で`mock`や`stub`の動きを確認してみると、想像がつきやすくなると思いました。（実行時に、`require 'minitest/autorun'`が必要です。）

# 参考情報
[RR / GitHub](https://github.com/rr/rr)
[RRのページ](http://rr.github.io/rr/)
[WebMock / GitHub](https://github.com/bblimke/webmock)
[MiniTest::Mock](http://docs.seattlerb.org/minitest/Minitest/Mock.html)
[MiniTest stub](http://docs.seattlerb.org/minitest/Object.html#method-i-stub)
[minitest-stub_any_instance](https://github.com/codeodor/minitest-stub_any_instance)
[Mock、Stub勉強会(ruby) ](https://www.slideshare.net/sonots/ruby-test-double-11533827)
[自動テストのスタブ・スパイ・モックの違い](https://gotohayato.com/content/483/)
[Test Double / xUnit Patterns.com](http://xunitpatterns.com/Test%20Double.html)
[minitest で stub, mock を使う](http://blog.jiikko.com/95)
[wiki テストダブル](https://ja.wikipedia.org/wiki/%E3%83%86%E3%82%B9%E3%83%88%E3%83%80%E3%83%96%E3%83%AB)

## [xUnit Test Pattern](http://xunitpatterns.com)
テストダブルのバリエーションを調べていると、こちらの[xUnit Test Patterns: Refactoring Test Code](https://www.amazon.co.jp/xUnit-Test-Patterns-Refactoring-Addison-Wesley-ebook/dp/B004X1D36K)の書籍がよく出てきました。
英語版しか出版されていないようですが、Webで内容を確認できました（英語です）。
http://xunitpatterns.com