0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[RSpec] WebMockで正規表現とArrayなクエリストリングを扱う

Last updated at Posted at 2020-09-15

はじめに

WebMockは昔からある有名なモックライブラリですが、RSpecでちょっとした細かい検証を行おうとした際にいろいろ試したことを自分用メモとしてまとめます。
主に外部API等へのリクエストを想定した、HTTPリクエストに対する検証の方法を取り扱っています。

環境

Ruby : 2.7.1
RSpec : 3.9.0
webmock : 3.8.3

テスト対象のコード

サンプルとして今回は、https://jsonplaceholder.typicode.com/ へリクエストします。

sample.ruby
require 'net/http'

class Sample
  def request(params = {})
    URI.parse('https://jsonplaceholder.typicode.com/todos')
      .tap { |uri| uri.query = URI.encode_www_form(params) }
      .then { |uri| Net::HTTP.get_response(uri) }
      .then { |res| res.body if res.is_a?(Net::HTTPSuccess) }
  end
end

基本形

webmockのstub_request を使用して、指定のURLへのリクエストをmockにします。
またexpect 時の検証には a_request を使うパターンで行っています。

sample_spec.rb
context '基本形' do
  before do
    stub_request(:get, 'https://jsonplaceholder.typicode.com/todos').and_return(status: 200, body: 'hoge')
  end

  it '正しくリクエストされること' do
    expect(Sample.new.request).to eq 'hoge'
    expect(a_request(:get, 'https://jsonplaceholder.typicode.com/todos')).to have_been_made.once
  end
end

context '基本形その2 クエリストリングも検証' do
  before do
    stub_request(:get, 'https://jsonplaceholder.typicode.com/todos?userId=2').and_return(status: 200, body: 'hoge')
  end

  it '正しくリクエストされること' do
    expect(Sample.new.request(userId: 2)).to eq 'hoge'
    expect(
      a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: { userId: 2 })
    ).to have_been_made.once
  end
end

正規表現でワイルドカード的なやつを使いたい

例えば、リクエストするURLをtodosからtodos/1に変えたい場合や、もっと複数のクエリストリングを指定したりしてテストしたい場合があると思いますが、その場合、毎回リクエストに合わせてstub_requestのURL文字列を変更しないとmockされません。それだと面倒なので、正規表現を使うことで対応します。

stub_request(:get, /https:\/\/jsonplaceholder.typicode.com/).and_return(status: 200, body: 'hoge')

こうすることで、https://jsonplaceholder.typicode.com/ ドメイン配下のリクエストはすべてmockされて、a_requestで検証可能になります。

また、正規表現はa_request でも使用することが可能です。

expect(a_request(:get, /https:\/\/jsonplaceholder.typicode.com/)).to have_been_made

ただし、以下のようにa_requestwithを使用したクエリストリングの検証はできません。

# この書き方はNG
expect(a_request(:get, /https:\/\/jsonplaceholder.typicode.com/).with(query: { userId: 2)).to have_been_made

Arrayなクエリストリングの検証方法

以下のような同一のキーを複数指定したクエリストリングの検証をしたい場合の方法を説明します。

Railsデフォルトな場合

Railsなアプリケーションの場合、以下のように[]を付けるのがデフォルトです。([]はエンコードされるので正確には%5B%5D)

?userId[]=1&userId[]=2&userId[]=3

以下のようにwithquery のhashで配列を指定すればOKです。railsっぽく書けます。

expect(
  a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: { userID: [1, 2, 3] })
).to have_been_made

Railsデフォルトじゃない場合

外部APIなどRails以外のアプリケーションの場合、以下のように[]を付けないパターンがほとんどです。

?userId=1&userId=2&userId=3

そういうときは、以下のように:flat_array というsymbolをWebMockに設定します。

WebMock::Config.instance.query_values_notation = :flat_array

検証方法は以下のようにwithquery hashで値を文字列で指定します。

expect(
  a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: 'userID=1&userID=2&userID=3')
).to have_been_made

文字列を直接書くのが嫌な場合は、URI.encode_www_form などを使用して文字列に変換すると良いでしょう。

expect(
  a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: URI.encode_www_form(userId: [1, 2, 3]))
).to have_been_made

flat_arrayを設定した場合はwithでクエリストリングがソートされる

WebMock::Config.instance.query_values_notation = :flat_array を設定した場合ですが、withでクエリストリングを検証するときはキーを文字順でソートした値を渡す必要があります。

表題のテスト対象のコードで以下を実行するとクエリストリングは userId=1&aa=1とリクエストされますが、 withqueryには並び替えた文字列aa=1&userId=1を渡さないとダメでした。

WebMock::Config.instance.query_values_notation = :flat_array

Sample.new.request(userId: 1, aa: 1)

# OK
expect(
  a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: 'aa=1&userId=1')
).to have_been_made

# hashはOK
expect(
  a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: { userId: 1, aa: 1 })
).to have_been_made

# これはNG
expect(
  a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: 'userId=1&aa=1')
).to have_been_made

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?