概要
RSpec で mock を使うには rspec-mocks
を使います
基本的な rspec-mocks
の使い方を紹介します
また Sinatra アプリと Sidekiq のワーカーのテストを書く際に rspec-mocks
を使ってテストする方法も紹介します
環境
- macOS 10.14.3
- Ruby 2.5.1p57
- sinatra 2.0.5
- sidekiq 5.2.5
- rspec, rspec-mocks 3.8.0
環境準備
今回は以下のような環境で行います
- Sinatra が動いている
- Sinatra が受けたリクエストは Sidekiq に流す
- Sidekiq のワーカーが外部のサイトにリクエストしてレスポンスを得る
テストを作成するケースとして今回は
- Sinatra アプリのテストを mock を使って実装する
- ワーカーのテストを mock を使って実装する
をやってみたいと思います
各種テスト用のコードは以下の通り
tree -I "vendor*|tmp*"
.
├── Gemfile
├── Gemfile.lock
├── app.rb
├── config.ru
├── lib
│ ├── my_http.rb
│ └── my_http.rbu
├── spec
│ ├── app_spec.rb
│ ├── spec_helper.rb
│ └── worker_spec.rb
└── worker.rb
vim Gemfile
gem "sinatra"
gem "rspec"
gem "rspec-mocks"
gem "rack-test"
gem "sidekiq"
cat app.rb
require 'sinatra/base'
require './worker.rb'
class Web < Sinatra::Base
get '/' do
Worker.perform_async
end
end
cat config.ru
require './app.rb'
run Web
cat worker.rb
require 'sidekiq'
require './lib/my_http.rb'
class Worker
include Sidekiq::Worker
def perform
res = MyHTTP.new.call
end
end
cat lib/my_http.rb
require 'net/https'
class MyHTTP
def call
uri = URI.parse 'https://kaka-request-dumper.herokuapp.com/'
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Get.new uri.request_uri
res = http.request req
res.code
end
end
アプリは
bundle exec rackup config.ru
ワーカーは
redis-server
bundle exec sidekiq -r ./worker.rb -P ./tmp/sidekiq.pid
で起動できます
これらのテストを mock を使って実装してみます
rspec テストの初期化
まずは spec ファイルを作成しましょう
bundle exec rspec --init
そしてそれぞれのテストを作成します
touch spec/worker_spec.rb
touch spec/app_spec.rb
Sinatra アプリのテスト
まずは Web アプリのテストを作成してみましょう
mock するのは Sidekiq のワーカーになります
vim spec/app_spec.rbv
ENV['RACK_ENV'] = 'test'
require './app.rb'
require 'rspec'
require 'rack/test'
describe "Web Tests" do
include Rack::Test::Methods
def app
Web
end
it "could be fetch the job id" do
allow(Worker).to receive(:perform_async).and_return("test_jid")
get '/'
expect(last_response.body).to eq "test_jid"
end
end
bundle exec rspec -f d spec/
でテストが実行されます
Worker.perform_async
を mock (どちらかと言えば stub) しているため実際に redis へはデータの登録は行われません
なのでワーカーは起動してない状態でもテストが通ります
ポイント
allow(Worker).to receive(:perform_async).and_return("test_jid")
これかなと思います
allow
は指定のクラスを mock するためのメソッドです
receive
に mock するメソッドを指定します
返り値も mock したい場合は続いて .and_return
で返り値を指定すれば OK です
これを get '/'
の前に設定するのもポイントです
get '/'
は実際に Siantra アプリにリクエストするので mock を作成スル前にコールしてしまうと redis にデータが登録されてしまいます
Sidekiq ワーカーのテスト
今度はワーカーのテストを書いてみます
先程とは違い mock するのは my_http.rb
になります
require './worker.rb'
describe "Worker Tests" do
it "could be fetch the response code" do
my_http = instance_double(MyHTTP, :call => 200)
allow(MyHTTP).to receive(:new).and_return(my_http)
w = Worker.new
expect(w.perform).to eq 200
end
end
Worker.perform
はクラスメソッドですが MyHTTP.call
はインスタンスメソッドになります
なので mock の作り方も少し異なります
まず instance_double
で MyHTTP.call
の mock を作ります
レスポンスは整数の 200
を返却するようにします
そして allow
で MyHTTP
インスタンスメソッドとして call
が呼ばれることを定義します
これで Sidekiq の Worker.perform
内で呼んでいる MyHTTP
を mock することができます
実際テストを実行してみるとわかりますが HTTP の通信をしていないことが確認できると思います
最後に
rspec-mocks
の簡単な使い方と Sinatra, sidekiq でのテストの書き方を紹介しました
まだまだいろいろな機能があるので興味があれば公式のページを見ると良いと思います
0 件のコメント:
コメントを投稿