質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

1回答

2839閲覧

Rspecのダブルの扱い方について

yusukexyusuke

総合スコア52

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2016/07/27 11:19

よろしくお願いしますm(__)m
Rspecでテストケースを書いていて疑問に思った点がありました。

自分なりに調べて理解したつもりなのですが、
私の解釈があっているかどうかチェックしてただけないでしょうか。

間違っている箇所がありましたら
ご指摘いただけると幸いです。

どうか宜しくお願い致します!!!!

///////////////////////////////ここから

テストケースでモックやスタブを作るということは
テストを走らせた時のアプリケーションの挙動を変更するということである。

ruby

1class Hoge 2 def hoge 3 "hogehoge" 4 end 5end

というメソッドがあって

ruby

1let(:class) { described_class.new } 2it do 3allow(class).to receive(:hoge).and_return('piyopiyo') 4end

というスタブを作ったら、
テストを実行し、hogeメソッドが実行された時に"piyopiyo"が返り値となるようになる。

binding.pryで止めてhogeを実行すると以下のようになる。

[1] pry(#ConfirmContractRenewal::Base)> hoge
"piyopiyo"

ちなみに

ruby

1allow(class).to receive(:hoge)

and_return('piyopiyo')を指定しないと、

[1] pry(#ConfirmContractRenewal::Base)> hoge
nil

となる

疑問点

今回疑問に思った箇所は

ruby

1# class 2class Hoge 3 NotificationMailer.delay.auto_contract_renewal(plan) 4end 5 6# rspec 7RSpec.describe Hoge do 8 let(:delay) { double('delay') } 9 it do 10 allow(NotificationMailer).to receive(:delay).and_return(delay) 11 expect(delay).to receive(:auto_contract_renewal).exactly(10).times 12 end 13end

ここのexpect(delay)の箇所でdelayが:auto_contract_renewalメソッドを持ってる体でテストを行えるのか?
が疑問だった。
だってdelayってただの空のダブルオブジェクトでしょ?

検証

本来はNotificationMailer.delayを実行すれば、delayメソッドがオブジェクト(以下オブジェクト①とする)を返す。
そしてそのオブジェクト①がauto_contract_renewalメソッドを持っている。

スタブを作成してテストを実行すると
本来NotificationMailer.delayが返すオブジェクトがダブルオブジェクトになる。

ダブルとは『代役』とか『影武者』という意味である。
ダブルオブジェクトとは①の代役である
なのでオブジェクト①が持っているメソッドを使えるように振る舞うことが出来る。

ここでもbinding.pryで止めて検証

ruby

1[16] pry(#<ConfirmContractRenewal::Base>)> NotificationMailer.delay 2#<Double "delay"> 3 4[18] pry(#<ConfirmContractRenewal::Base>)> NotificationMailer.delay.class 5RSpec::Mocks::Double < Object 6 7[19] pry(#<ConfirmContractRenewal::Base>)> NotificationMailer.delay.methods.grep(/auto/) 8[ 9 [0] auto_contract_renewal(*args, &block) #<Double "delay"> 10 [1] autoload_all(*paths) RSpec::Mocks::Double (RequireAll) 11 [2] autoload_rel(*paths) RSpec::Mocks::Double (RequireAll) 12]

となる。
ダブルオブジェクトがauto_contract_renewalと持っている。
これが

ここのexpect(delay)の箇所でdelayが:auto_contract_renewalメソッドを持ってる体でテストを行えるのか?

が疑問だった。

の答えになった。

///////////////////////////////ここまで

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

質問の意図からずれてたら済みませんが、以下見解です。

ruby

1expect(delay).to receive(:auto_contract_renewal).exactly(10).times

このコード記述の時点ではdelay.auto_contract_renewalが呼ばれるというわけでは無く、「delayオブジェクトは10回auto_contract_renewalメソッドが呼ばれる」というモック(テスト仕様)を定義したことになるので、RSpecはこのテスト仕様の通りauto_contract_renewalが呼ばれるかをチェックするためにメソッドを用意してメソッドの呼び出される回数のカウントを開始しているのだろうと思います。

疑問点にかかれたHogeクラスとそのテストケースだとdelayメソッドが呼ばれるのはクラスロード時の1回のみですので、it内で丁度10回auto_contract_renewalメソッドが呼ばれるトリガーとなる呼び出しが存在しないため必ずテストは失敗するかと思います。

テストとして成り立たせるためにはモックを記述した後にそれが指定したとおりに呼ばれるためのトリガとなるコードを記述させなくてはいけません。クラスロードだと呼び出しにくいのですが、、、例えばauto_contract_renewalが2回必ず呼ばれるメソッドがあったとしたら、それを5回呼び出してみる等になります。

投稿2016/08/16 14:59

toshi3221

総合スコア34

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yusukexyusuke

2016/08/29 23:53

> toshi3221さん ご回答ありがとうございます! > it内で丁度10回auto_contract_renewalメソッドが呼ばれるトリガーとなる呼び出しが存在しないため必ずテストは失敗するかと思います。 処理の記述を端折ってしまったのですが、クラスの中で NotificationMailer.delay.auto_contract_renewal(plan) をn回呼ぶ処理があります。 > it内で丁度10回auto_contract_renewalメソッドが呼ばれるトリガー とは例えばどのような記述を書かれたりするのでしょうか!? 教えていただけないでしょうか。 宜しくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問