読者です 読者をやめる 読者になる 読者になる

やーまんぶろぐ

気が向いた時にだけ書くブログ

VCRを使ってRSpecのwebmockを作成する方法

前回、RubyGemを自作してローカルで動かす方法について書きました。
yamano3201.hatenablog.jp

前回記事にも書きましたが、THETAのgemを自作しようとしています。
APIの中身はこちらで確認できます。
API Reference · v2 · API & SDK | RICOH THETA Developers

THETAのAPIを叩くためにはWi-Fiで接続する必要があります。
gemを作成している間ずっとWi-Fiで繋いでるわけにもいかないので、まずはmockについて調べました。

そしたらVCRという便利なものがありました。知らなかったです。
morizyun.github.io

VCRを使ってRSpecのwebmockを作成してみたのでメモしておきます。
gemを作成する目線で書いています。

VCRとは

VCRとは、webmock用のデータを自動で作成してくれるgemです。
実際にHTTP通信した結果を記録して、mockを作成してくれます。

gemspec作成

さっそくmockを作成する手順を書いていきます。
まずは、ruby_theta.gemspecに下の3つを追加します。

  spec.add_development_dependency "vcr"
  spec.add_development_dependency "webmock"
  spec.add_dependency "httparty"

追加したらbundle installしておきます。

spec_helper.rb に追加

つづいてspec_helper.rb に下の内容を追加します。

require 'vcr'

VCR.configure do |config|
  config.cassette_library_dir = "spec/fixtures/vcr_cassettes"
  config.hook_into :webmock
  config.default_cassette_options = { record: ENV.fetch('RECORD'){ :once }.to_sym }
end

cassette_library_dir で指定したディレクトリにmockがymlとして作成されます。

default_cassette_options のrecord値にallを指定することで記録状態を変更することができます。

このオプションを指定しない場合は、初回のみHTTP通信を行いそのときの状態を記録してくれます。
2度目以降の実行ではmockを使用するのでHTTP通信は発生せず記録状態も変更されません。

作成中は記録状態を変更したいこともあるので、コマンドラインから切り替えられるようにrecodeの値に環境変数を指定できるようにしました。
qiita.com

後でも書きますが、RECORDにallを指定してテストを実行すると、記録状態を上書きして変更できます。

specファイル

例としてTHETAとセッションを張るspecを追加してみました。

spec/ruby_theta_spec.rb

require 'spec_helper'

describe Theta do
  let(:theta) { Theta.new }
  context 'commands' do
    it 'startSession' do
      VCR.use_cassette("start_session") do
        response = theta.start_session
        expect(response.code).to eq(200)
      end
    end
  end
end

ここでは200が返ってくることしかテストしてません。気が向いたらbodyの中身とかテストしてもいいかもしれません。
このタイミングでbundle exec rake spec してテストが失敗することを確認しておきます。

テスト対象コードを作成

上で書いたテストを成功させるための、テスト対象コードを作成していきます。

lib/ruby_theta/ruby_theta.rbにinitializeを書いていきます。

require "httparty"
require "json"
require "ruby_theta/modules/commands"

class Theta
  module Modules; end
  include HTTParty
  include Theta::Modules::Commands

  def initialize(options={})
    @theta_ip = options[:ip] || '192.168.1.1'
    self.class.base_uri @theta_ip
  end
end

つづいて、lib/ruby_theta/modules/commands.rb にセッションを張るコードを作成します。

class Theta
  module Modules
    module Commands
      def start_session
        self.class.post("/osc/commands/execute", {:body => {"name" => "camera.startSession"}.to_json})
      end
    end
  end
end

RSpec実行

上でも書きましたがallを指定すると、記録状態を上書きして変更することができます。
実行前にTHETAとPCをWi-Fiで接続しておきます。

bundle exec rake spec

or

RECORD=all bundle exec rake spec

mockの中身

RSpecを実行すると、実際のHTTP通信の結果がyamlとして記録されます。

spec/fixtures/vcr_cassettes/start_session.yml

---
http_interactions:
- request:
    method: post
    uri: http://192.168.1.1/osc/commands/execute
    body:
      encoding: UTF-8
      string: '{"name":"camera.startSession","parameters":{}}'
    headers:
      Accept-Encoding:
      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
      Accept:
      - "*/*"
      User-Agent:
      - Ruby
  response:
    status:
      code: 200
      message: OK
    headers:
      Server:
      - server
      Cache-Control:
      - no-cache, no-store, max-age=0, must-revalidate
      Pragma:
      - no-cache
      Expires:
      - '0'
      Max-Age:
      - '0'
      X-Content-Type-Options:
      - nosniff
      Connection:
      - close
      Accept-Ranges:
      - bytes
      Content-Length:
      - '95'
      Content-Type:
      - application/json; charset=utf-8
    body:
      encoding: UTF-8
      string: '{"name":"camera.startSession","state":"done","results":{ "sessionId":"SID_0001","timeout":180}}'
    http_version: 
  recorded_at: Sun, 24 Apr 2016 11:32:18 GMT
recorded_with: VCR 3.0.1

requestとresponseの中身が確認できますね。
以上で1ケースですが、VCRを使ってRSpecのwebmockが作成できました。

最後に

ちゃんとテストを書きながらgemを作成したかったので、2,3年前に買ったRSpec Bookを読み返しました。

The RSpec Book (Professional Ruby Series)

The RSpec Book (Professional Ruby Series)

RSpecのバージョンが古かったのもあるし、mockについてはVCRが便利すぎたのでそこまで参考にはならなかったのですが、復習にはなりました。

mockを使った単体テストはこれでサクッと書けそうです。
セッション張って撮影して画像を取得してセッション閉じるといったようなユースケースごとのテストを、mockを使って書けると良いですね。

ユースケースごとのテストは実機でもテストできるようにしておくと、さらに安心ですかね。

気が向いたら、また書きます。