WebMock is a well-known mock library that has been around for a long time, but I will summarize what I tried variously when trying to do a little detailed verification with RSpec as my own memo. We mainly handle verification methods for HTTP requests, assuming requests to external APIs.
Ruby : 2.7.1 RSpec : 3.9.0 webmock : 3.8.3
As a sample, this time, I will request to 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
Use webmock's stub_request
to make a request to the specified URL a mock.
In addition, the verification at the time of ʻexpect is performed by the pattern using ʻa_request
.
sample_spec.rb
context 'Uninflected word' do
before do
stub_request(:get, 'https://jsonplaceholder.typicode.com/todos').and_return(status: 200, body: 'hoge')
end
it 'To be requested correctly' 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 'Uninflected word 2 Verify query string' do
before do
stub_request(:get, 'https://jsonplaceholder.typicode.com/todos?userId=2').and_return(status: 200, body: 'hoge')
end
it 'To be requested correctly' 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
--have_bee_made
is a matcher for RSpec provided by WebMock. There are several other matchers available.
with
.
--Even if you don't use with
, you can write ʻa_request (: get,'https://jsonplaceholder.typicode.com/todos?userId=2')in a straightforward manner. --In addition to the query string,
with also allows validation using the value
body
headers`` basic_auth`.For example, you may want to change the URL you request from todos
to todos / 1
, or you may want to test by specifying more query strings, in which case you may want to match each request If you do not change the URL string of stub_request
, it will not be mocked. That's a hassle, so use regular expressions to handle it.
stub_request(:get, /https:\/\/jsonplaceholder.typicode.com/).and_return(status: 200, body: 'hoge')
By doing this, all requests under the https://jsonplaceholder.typicode.com/
domain will be mocked and can be verified with ʻa_request`.
Regular expressions can also be used in ʻa_request`.
expect(a_request(:get, /https:\/\/jsonplaceholder.typicode.com/)).to have_been_made
However, you cannot validate the query string using with
in ʻa_request` as shown below.
#This way of writing is NG
expect(a_request(:get, /https:\/\/jsonplaceholder.typicode.com/).with(query: { userId: 2)).to have_been_made
This section describes the method when you want to verify the query string that specifies multiple identical keys as shown below.
For Rails applications, the default is to add []
as shown below. ([]
is encoded so it's exactly % 5B% 5D
)
?userId[]=1&userId[]=2&userId[]=3
It is OK if you specify the array in the hash of query
in with
as shown below. You can write like rails.
expect(
a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: { userID: [1, 2, 3] })
).to have_been_made
For non-Rails applications such as external APIs, most patterns do not include []
as shown below.
?userId=1&userId=2&userId=3
In such a case, set the symbol : flat_array
in WebMock as shown below.
WebMock::Config.instance.query_values_notation = :flat_array
The verification method is to specify the value as a string with the query
hash of with
as shown below.
expect(
a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: 'userID=1&userID=2&userID=3')
).to have_been_made
If you don't like to write the string directly, you can convert it to a string using ʻURI.encode_www_form` etc.
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
, the query string is sorted by with
If you set WebMock :: Config.instance.query_values_notation =: flat_array
, but when validating the query string with with, you need to pass the values sorted by character order of the keys.
The query string is requested as ʻuserId = 1 & aa = 1 when the code under test in the title is executed, but the sorted string ʻaa = 1 & userId = 1
is passed to query
of with
. It was useless without it.
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 is OK
expect(
a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: { userId: 1, aa: 1 })
).to have_been_made
#This is NG
expect(
a_request(:get, 'https://jsonplaceholder.typicode.com/todos').with(query: 'userId=1&aa=1')
).to have_been_made
https://github.com/bblimke/webmock
Recommended Posts