Geocoderをrspecでテストするときのstubの書き方


# 概要

```
Geocoder.search('検索キー')
```

をrspecのテストで使いたいとき、stubは以下の通り。

```ruby
before do
	Geocoder.configure(lookup: :test)
	Geocoder::Lookup::Test.add_stub('検索キー',[検索結果のハッシュ])
end
```

# 



# 説明
## stubを使わなかった時のエラー例
Geocoder.search('検索キーワード')を使ったコードに対して、ネットワーク接続が出来ない環境でrspecを実行するとエラーがでる。

これは、[git geocoder](https://github.com/alexreisner/geocoder#testing-apps-that-use-geocoder)に書かれているとおり、'検索キーワード'に対応するstubを作れば解決する。
以下に、解決例を書く。

## 解決例
### シナリオ
place_search_controllerというコントローラに ``/place_search/?keyword=東京`` のルーティングでアクセスし、keywordを元にGeocode.search(keyword)を使う機能を実装後、テストコードを書く。


### 実装した機能
Geocoderを使ってkeywordで検索したとき、#indexは以下の結果を返す

* 結果が得られればステータス200でview（views/place_search/index.html.erb）を表示
* 結果が得られなければステータス500でview（errors/error_500.html.erb）を表示する

```app/controllers/place_search_controller.rb
class PlaceSearchController < ApplicationController
	def index
		geo = Geocoder.search(params[:keyword])
		if geo.present?
			@location = Geocoder.search(params[:keyword])[0].data["geometry"]["location"] # latitudeとlongitudeを取得して、viewで使うことを想定した動き（viewは省略）
		else
			render template: 'errors/error_500', status: 500, layout: 'application', content_type: 'text/html' # 検索ヒットが0件の時は 500番を返す
		end
	end
end
```

### rspecテストコード

* keyword="東京"で検索したときはステータス200番を返す
* keyword="ダメなキーワード"で検索したときはステータス500番を返す

上記2点をテストするコードは以下の通り


```spec/controllers/place_search_controller_spec.rb
require 'spec_helper'

describe PlaceSearchController do
	describe '#index' do
		before do
			Geocoder.configure(lookup: :test)
        	Geocoder::Lookup::Test.add_stub(
          		'東京', [{
            	'geometry' => { 'location' => { 'lat' => 35.7090259, 'lng' => 139.7319925 } }
          	}]
        	)
        	Geocoder::Lookup::Test.add_stub(
          		'ダメなキーワード', []
        	)
		end
		it '東京で検索したときはステータス200番を返す' do
			get '/place_search', {keyword: '東京'}
         	expect(response.status).to eq 200

		end
		it 'ダメなキーワードで検索したときはステータス500番を返す' do
			get '/place_search', {keyword: 'ダメなキーワード'}
         	expect(response.status).to eq 500
		end
	end
end
```

これでOK。

# 解説

Geocoder.search('検索キー')は、検索キーを位置情報API（初期値はgoogle）に問い合わせることで位置情報を得ています。よって、外部ネットワークに接続できない場合はGeocoder.searchが機能しないため、関連するテストコードが全てエラーとなります。
テストのためだけに、外部ネットワークに無駄にアクセスすることは推奨されないため、CI環境では外部へのネットワーク接続が不可の場合があります。
このときに使うのがstubという仕組みであり、Geocoderではすでにその仕組みを用意していますので、活用するだけです。

```
Geocoder.configure(lookup: :test)
```

lookupは検索するapiを設定するためのキーです。これをtestと指定することにより、以降のGeocoder.searchが以下で示すstubに対して行われるようになります。

```
Geocoder::Lookup::Test.add_stub('東京', [ 省略 ])
```

stubを指定します。フォーマットは、 ``Geocoder::Lookup::Test.add_stub('検索キー',[検索結果のハッシュ])`` です。
検索キーは ``Geocoder.search('東京')`` であれば __東京__ です。
検索結果のハッシュは、Geocoder.configureの設定によって異なります。わからない場合は、  ``rails c`` で 下記のコマンドを実行してその結果を入れてください。

```
> Geocoder.search('検索キー')[0].data.to_hash
```


例えば検索キーとして __東京__ を指定した場合は以下の通りなので、それを検索結果のハッシュとして貼り付ければstubとして使えます。（以下は見やすいように整形しています。）

```Geocoder.search('東京')[0].data.to_hash
{ 'address_components' => [{ 'long_name' => 'Tokyo',
                             'short_name' => 'Tokyo',
                             'types' => %w(colloquial_area locality political) },
                           { 'long_name' => 'Tokyo', 'short_name' => 'Tokyo', 'types' => %w(administrative_area_level_1 political) },
                           { 'long_name' => 'Japan', 'short_name' => 'JP', 'types' => %w(country political) }],
  'formatted_address' => 'Tokyo, Japan',
  'geometry' => { 'bounds' => { 'northeast' => { 'lat' => 35.8175167, 'lng' => 139.9198565 },
                                'southwest' => { 'lat' => 35.5208603, 'lng' => 139.5629048 } },
                  'location' => { 'lat' => 35.7090259, 'lng' => 139.7319925 },
                  'location_type' => 'APPROXIMATE',
                  'viewport' => { 'northeast' => { 'lat' => 35.8175167, 'lng' => 139.9198565 },
                                  'southwest' => { 'lat' => 35.5208631, 'lng' => 139.5629048 } } },
  'place_id' => 'ChIJXSModoWLGGARILWiCfeu2M0', 'types' => %w(colloquial_area locality political) }


```

なお、検索結果のハッシュは機能から呼び出している箇所の指定だけでもいいです（もちろん全部書いてもいいですが、冗長ですよね）。今回の機能では、'location'の箇所しか呼び出していないので、以下箇所だけstubに記述しました。

```
Geocoder::Lookup::Test.add_stub(
          		'東京', [{
            	'geometry' => { 'location' => { 'lat' => 35.7090259, 'lng' => 139.7319925 } }
          	}]
        	)
```