起こったこと
Faradayを使って立てたAPIサーバへアクセスするテストを、webmockを使って行いました。
下記のような実装にしたところ、テストを実行してもwebmockが期待通り動作しません。
client.rb
class Client
def user
conn = Faraday.new(url: 'http://example.com/api/v1')
res = conn.get '/register'
end
res.status
end
end
テスト内ではAPIエンドポイントとなるURL(http://example.com/api/v1/register
)をスタブ化しているのですが、指定したステータスコードの200ではなく404が返ってきます。
client_spec.rb
require 'rails_helper'
require 'webmock/rspec'
describe do
before do
# 外部API呼び出し処理に対し、スタブを登録する
stub_request(:get, 'http://example.com/api/v1/register').to_return(status: 200)
end
it 'Status Code 200を返すこと' do
response = Client.new.user
expect(response).to eq 200
end
end
検証したところ、パスの指定がおかしいという結論に至りました。
原因と対処法
Faradayでは、パスを書くときの決まりがありました。
client.rb
class Client
def user
conn = Faraday.new(url: 'http://example.com/api/v1')
# 先頭にスラッシュを入れてはいけない!
res = conn.get 'register' #=> GET http://example.com/api/v1/register
...
Faradayを使ってGETする際に、先頭にスラッシュを入れない状態だと、URLは相対パスとして扱われます。設定したとおりです。
これが期待通りの挙動です。
一方、先頭にスラッシュを入れていると、FaradayによってURLは絶対パスとして扱われ、ドメイン部分のみhttps://example.com
がURLとされてしまいます。ドメイン以下のパス /api/v1
は破棄されます。
client.rb
class Client
def user
conn = Faraday.new(url: 'http://example.com/api/v1')
# 先頭にスラッシュを入れると/api/v1が破棄されてしまう
res = conn.get '/register' #=> GET http://example.com/register
http://example.com/register
に対してGETしてしまっています。
この動作に関して、issueが上がっていました。
なぜこの仕様なのだろう
一般にUNIXではルートを起点として絶対パスを指します。
先頭にスラッシュを入れることで、Faradayではルートからの絶対パスを指定したとみなします。
このためWebサイトのルートディレクトリであるドメイン部分を残し、それ以下のパスは破棄されるそうです。
なるほど…。
READMEのサンプルが先頭スラッシュで始まっているので、このような間違いを誘うのかもしれません。
conn = Faraday.new(:url => 'http://www.example.com')
response = conn.get '/users' # GET http://www.example.com/users'
追記
Faradayの面白い仕様(先頭スラッシュにするとバスを削除しちゃうやつ)、issueあげたらREADMEに挙動を追記してくれました!
README:
conn = Faraday.new(:url => 'http://www.example.com/api')
response = conn.get 'users' # GET http://www.example.com/api/users'
# You can override the path from the connection initializer by using an absolute path
response = conn.get '/users' # GET http://www.example.com/users'
これでもう間違えない。