猫と一緒にガジェットライフ♪ムチャ(@mutoj_rdm821)です。
昨日書けなかった具体的なソースを紹介していこうと思います。お題は「Rubyの単体テストにRSpecを用いて、ActiveRecord::Baseを継承したモデルクラスのモックを作ってテストを行う方法」です。
シチュエーション
今作っている機能は、クライアントからの要求を受けてその内容を解析し、適切なモデルクラスを生成した後に、その結果を返す部分です。
モデルクラスは並行して別の人が作っているため、まだ実体はありません。この部分をモックで置き換えます。
RSpecは2.14.??を使っています。あ、バージョン調べてくるの忘れた(;´∀`)
以下のようなSampleModeleクラスがあったとして。
class SampleModel < ActiveRecord::Base #コンストラクタはオーバーライドしてない #def initialize : end
モックオブジェクトを作るには、doubleメソッドを使います。同時にインスタンス変数もセットできます。
#モックオブジェクトを生成する mock = double(“TestModel”) #インスタンス変数もセットしておく場合 mock = double(“TestModel”, :id => 123, :name => “test” ・・・・)
SampleModelを生成するところがあるわけですが、それをこのモックオブジェクトに差し替えたいです。
ActiveRecord::Base.stub(:new).and_return(mock)
対象のモデルクラスはコンストラクタ(initialize)をオーバーライドしていないため、スーパークラスのActiveRecord::Baseの:newシンボルを差し替えます。(Rubyでは文字列を表すのにシンボルという方法があってうんたらかんたら・・・というのはまた別の機会に)
stubメソッドで置き換え対象のメソッドを指定し、and_returnメソッドにその戻り値を指定します。こうすると、SampleModelクラスのインスタンスを生成しようとしたときに、実際は生成コードが実行されずに、単にmockを返すだけの動きになります。
また、生成後に別のメソッドも置き換えたい場合は以下のようにします。
#メソッド呼び出しを何も実行しないように置き換える mock.stub(:create).and_return() #対象のメソッドが、適切な引数で呼び出されたかを検証する mock.should_receive(:find).with(10)
stubメソッドで指定したメソッドは、実際のメソッドが呼ばれず、何もせずに抜けます。戻り値が指定されていればそれが返ります。
テストなので検証する必要がある場合、should_receiveメソッドでメソッド名を指定すると、テスト中に呼び出されなかった場合はテストが失敗します。さらにwithメソッドに渡されるべき引数を指定しておくと、呼び出されても引数が異なる場合はテストが失敗します。
この辺の条件は様々な指定ができて、「once」で1回だけ、「twice」で2回、exactly(n).timesでそれ以外の回数とかたくさんあります。以下の記事がまとまっています。
Ruby on Rails: The RSpec Book 読破記録: 14章から | DYO.JP ver.2
ところが最近は違うらしい
というわけで目的は果たせたのですが、RSpecの構文は今転換期を迎えていて、should~でなくてexpect~を使うようになっているようです。上のコードは以下のようになる模様(帰りの電車でわかったのでまだ試してません・・・)
allow(ActiveRecord::Base).to receive(:new).and_return(mock) #メソッド呼び出しを何も実行しないように置き換える allow(mock).to receive(:create).and_return() #対象のメソッドが、適切な引数で呼び出されたかを検証する expect(mock)to receive(:find).with(10)
他にも細かい内容は以下のページに(英語)
なぜshouldではなくexpectを使うようになったかは、以下を辿っていくとわかるかもしれません。
Ruby - RSpecのshouldはもう古い!新しい記法expectを使おう! - Qiita [キータ]
というわけでRubyのRSpecを使ったテストにおける、モックの使い方でした。
もちろんこれを使って適当に呼び出しを検証してもダメで、「適切な引数を渡していること」「想定する戻り値を返したときにきちんと動くこと」を検証しなければなりません。でないと実際に結合して動かしたときに動かなくて、「お前は何をやっていたんだ」と言われてしまいます。
新しい事ばかりで問題が山積みですが、1つ1つ解決していこうと思います。
それではみなさまよきガジェットライフを(´∀`)ノ