こんにちは。seimiyajunです
横浜にあるコワーキングスペースでRuby on Railsのもくもく会を月に1回、2年半くらいやっています
今回はタネマキ Advent Calendar 2018の20日目ということで書きます
タネマキ Advent Calendar 2018自体は1ミリも技術系のアドベントカレンダーではないのですが気にせずいきたいと思います
概要
環境
Ruby 2.5.1
RSpec 3.8.1
やりたいこと
- 架空のWebサービス microtaskhubをAPI経由で利用し、その結果をAPIで返却する
- 外部APIを使うメソッドにおいて、create, deleteを実装する
- RSpecのスタブを使ってテストをスムーズに行う
流れ
- createメソッドとdeleteメソッドを書く
- テストコードを書く
開発
1. createメソッドとdeleteメソッドを書く
えいや、とこんな感じで。
http_methodというメソッドにparamsを渡せばいい感じにやってくれる前提です
class Microtaskhub
def create(username)
params = {
method: 'post',
url: 'https://microtaskhub.net',
username: username
}
response = http_method(params)
"#{response['header']} : #{response['body']}"
end
def destroy(useranme)
params = {
method: 'delete',
url: 'https://microtaskhub.net',
username: username
}
response = http_method(params)
"#{response['header']} : #{response['body']}"
end
private
def http_method(params)
# 本来はNet::http等で処理を書く
{'header' => 200, 'body' => 'Success!'}
end
end
2. テストコードを書く
Requestが成功した場合
これはcreateについてはそのままテストが書けそうです
RSpec.describe Microtaskhub do
describe 'create' do
context 'request is succeed' do
it 'returns success message' do
expect(Microtaskhub.new.create('test')).to eq '200 : Success!'
end
end
end
end
これでも動作はしますが、問題点があります
- 毎回APIを叩くので速度が遅い (1つだけなら気にならないかもしれませんが、こういったテストが30、100と増えていくと処理時間が問題になります)
- テストを実行するたびにmicrotaskhubでのユーザー数がどんどん増えていく (ユーザー数に上限があったり、課金ポイントになっているような場合は問題になります)
- ユニークネームでないと登録できない場合、ランダムな名前でcreateする必要が出てくる (fixtureなどを使えば解決できますが、これは本質ではないですね)
- microtaskhub側でcreateできない場合、ユーザーを削除のテストができない (招待してメール承認のフローがある場合などはcreateしただけでは削除ができません)
ということでここはstub化しましょう
RSpec.describe Microtaskhub do
describe 'create' do
context 'request is succeed' do
it 'returns success message' do
microtaskhub = Microtaskhub.new
allow(microtaskhub).to receive(:http_method) { {'header' => 200, 'body' => 'test is created'} }
expect(microtaskhub.create('test')).to eq '200 : test is created'
end
end
end
end
既存コードの http_method
をallow(microtaskhub).to receive(:http_method)
という記述で上書きしています
処理内容は{}
内です。
なお処理が複数行ある場合は下記のようにdo〜end
で書くこともできます。
allow(microtaskhub).to receive(:http_method) do
#処理内容をここに書く
end
なおdestroy
もこんな感じで書けます
describe 'destroy' do
context 'request is succeed' do
it 'returns success message' do
microtaskhub = Microtaskhub.new
allow(microtaskhub).to receive(:http_method) { {'header' => 204, 'body' => 'No Content.'} }
expect(microtaskhub.destroy('test')).to eq '204 : No Content.'
end
end
end
超重要:実際に対象のサービスがどのような結果を返してくるか、については事前に確認しておくこと
stubの活用例:例外
timeout
を実際に意図的に発生させるのは無理なので、stubに例外を投げてもらいます。
context 'timeout' do
it 'returns timeout message' do
microtaskhub = Microtaskhub.new
allow(microtaskhub).to receive(:http_method) { raise Timeout::Error.new }
expect(microtaskhub.create('test')).to eq '408 : Request Timeout.'
end
end
プロダクトコード側では例外をキャッチするようにします
(ここで例外をキャッチするのはすごくいけていないので参考にしないようにお願いします)
def create(username)
params = {
method: 'post',
url: 'https://microtaskhub.net',
username: username
}
response = http_method(params)
"#{response['header']} : #{response['body']}"
rescue Timeout::Error
'408 : Request Timeout.'
end
ここまでのコードをリポジトリにまとめてあります。
改めてタネマキRailsもくもく会
私が主催でRuby on Rails もくもく会@タネマキを月1回開催しています。
初心者歓迎ですので、もしご興味がありましたら参加ください