RSpecコトハジメ ~初期設定マニュアル~

# はじめに
RSpecをRailsアプリで使用する際、「どのgem入れなきゃいけないんだっけ」「あの設定ってどうやって書くんだっけ？」といったことが多かったので、自分なりの初期設定をまとめてみました。




## support配下のファイル有効化
RSpecのテストで共通化したい処理は、モジュールにしてspec/support配下に置くのがお作法。

今回は、

- spec内で共有したい処理は`spec/support/helper`ディレクトリ配下にモジュールを作成
- initialize時に読み込む設定ファイルは`spec/support/config`ディレクトリ配下に作成

という方針をとり、それぞれ`spec/rails_helper.rb`から読み込むよう設定。


```ruby:spec/rails_helper.rb
Dir[Rails.root.join("spec/support/config/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/helper/*.rb")].each { |f| require f }
```

指定したテストタイプで、helper配下に作成したモジュールを読み込むことで、spec内でモジュールに定義された処理が使用できる。

```ruby:spec/rails_helper.rb
RSpec.configure do |config|
  config.include ◯◯(モジュール名), type: :☓☓(使用するテストのタイプ)
end
```

各spec内で読み込むmoduleを都度指定しても良い。

```ruby:specファイル
RSpec.feature 'Home', type: :system do
  include ◯◯(モジュール名)
end
```






## gemの追加
諸々必要となるgemを追加し、設定を施していきます。




#### rspec-rails
RSpecの機能をRailsで使用するためのgem

```ruby:Gemfile
gem 'rspec-rails'
```

gem追加後、下記コマンドを実行

```
$ rails g rspec:install
```

コマンド実行後、下記ファイルが作成される

```
create .rspec
create spec/spec_helper.rb   #RSpecの全体的な設定を書く
create spec/rails_helper.rb  #Rails特有の設定を書く
```





#### factory_bot
テストデータ生成用のgem

```ruby:Gemfile
gem 'factory_bot_rails'
```

下記設定により、FactoryBotのデータ呼び出しを簡略化できる。
(テストデータの呼び出しを、`FactoryBot.create(:◯◯)` → `create(:◯◯)`に簡略化出来る)

```ruby:spec/support/config/factory_bot.rb
require 'factory_bot'

RSpec.configure do |config|
 config.include FactoryBot::Syntax::Methods
end
```

また、springを使用してrspecを動かしていると、factoryで作成したデータが正しく読み込まれないことがある。
そんな時は、下記を設定ファイルに追記して、毎回全てのexample実行前にfactory_botを再読込させる。

```ruby:spec/support/config/factory_bot.rb
require 'factory_bot'

RSpec.configure do |config|
 config.before :all do
  FactoryBot.reload
 end
end
```







#### spring-commands-rspec
springでrspecを使用することで高速化を図る。

```ruby:Gemfile
gem 'spring-commands-rspec'
```

gem追加後、下記コマンド実行で、bin/rspecが作られる

```
$ spring binstub rspec
```

bin/rspec作成後は、下記コマンド実行でrspecをspringで使用できる。

```
$ bin/rspec
```





#### capybara
インテグレーション・テストの補助ツール

```ruby:Gemfile
gem 'capybara'
```

capybaraの読み込み

```ruby:spec/support/config/capybara.rb
require 'capybara/rspec'
```





#### launchy
feaatureテスト中に`save_and_open_page`を記載することで、キャプチャのHTMLを自動で開くことが出来るgem

```ruby:Gemfilr
gem 'launchy'
```





#### chromedriver-helper
seleniumとchromeのインターフェース

```ruby:Gemfile
gem 'chromedriver-helper'
```

jsを使用するsystem specの場合はchromeのヘッドレスドライバを使用するよう設定する。
(※jsを使用しないテストの場合は、高速なRack::Testドライバを使用)

```ruby:spec/support/config/capybara.rb
require 'capybara/rspec'

RSpec.configure do |config|
 config.before(:each, type: :system) do
  driven_by :rack_test
 end

 config.before(:each, type: :system, js: true) do
  driven_by :selenium_chrome_headless
 end
end
```





#### shoulda-matchers
リッチなマッチャが使えるようになる。
[shoulda-matchers](https://github.com/thoughtbot/shoulda-matchers)

```ruby:Gemfile
gem 'shoulda-matchers'

# rails5以降の場合、 
gem 'shoulda-matchers', git: 'https://github.com/thoughtbot/shoulda-matchers.git', branch: 'rails-5'
```

requireした上で、初期設定を施す。

```ruby:spec/support/config/shoulda_matcher.rb
require 'shoulda/matchers'

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end
```





#### VCR
API呼び出し時に、一回目の呼び出しを記録して、次回以降は記録されたレスポンスを返してくれる
(ちなみに、VCRは、Video Cassette Recordeの略)

```ruby:Gemfile
gem 'vcr'
gem 'webmock' #vcrの処理時に水面下で利用されるHTTPスタブ化ライブラリ
```

外部HTTPリクエスト送信時にVCRを使用する設定を追加

```ruby:spec/support/config/vcr.rb
require 'vcr'

VCR.configure do |config|
 config.cassette_library_dir = "#{ ::Rails.root }/spec/cassettes"
 config.hook_into :webmock
 config.ignore_localhost = true
 config.configure_rspec_metadata!
end
```

exampleのオプションに`vcr: true`をつけることで、外部HTTPリクエストがVCR経由で行われるようになる。

```ruby
it 'hoge', vcr: true do
 exampleの中身
end
```






#### simplecov
rspec実行時にcoverageというフォルダが自動で作成され、その中にテストのカバレッジを計測したHTMLファイルを自動で配置してくれるようになる。

```ruby:Gemfile
gem 'simplecov'
```

```ruby:spec/rspec_helper.rb
require 'simplecov'

SimpleCov.start 'rails'
```

あとはいつも通りテストを実行するだけ。




#### database_cleaner
テスト毎にDBをリセットすることで、テストの独立性を担保できる。

```ruby:Gemfile
gem 'database_cleaner'
```

DatabaseCleanerの設定を追加

```ruby:spec/support/config/database.rb
RSpec.configure do |config|
  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
  end
  config.before(:each) do
    DatabaseCleaner.start
  end
  config.after(:each) do
    DatabaseCleaner.clean
  end
end
```
これで、specを実行するたびにDB内のデータが削除される。

※Rails5.1以上では、デフォルトでDatabaseCleanerが組み込まれているため、gemをインストールする必要はありません。





## 初期設定に含めていないgem達
名前はよく聞くけども、各々の理由で初期設定に含めていないgemも列挙しておきます。
必要に応じて追加してください。

#### faker
ダミーデータの生成を楽にしてくれるgem。
特に必要性を感じてないので入れてないです。

#### funbar
specの進捗状況を可視化してくれるgem(全体でいくつexampleがあって、そのうち何個終了したか等が見やすくなる)
むしろ見づらくなってる気がするので入れてないです。(主観ですw)












## .rspec
RSpecの設定ファイル
下記のように設定。

```bash:.rspec
--color
--require spec_helper
--format documentation
```





## タグ
各exampleに、独自のタグを設定することが出来る。

focusタグがついたspecがあればそれだけ実行、なければ全spec実行する設定を追加。

```ruby:spec/spec_helper.rb
RSpec.configure do |config|
  config.filter_run focus: true
  config.run_all_when_everything_filtered = true
end
```

exampleにfocusタグを付ける場合は、こんな感じ

```ruby:サンプルexample
it 'hoge', :focus do
 exampleの中身
end
```





## seedデータの挿入
テスト開始前にDBにデータを入れておきたい場合、ありますよね。
僕はいつもseed-fuを使用しているので、

```ruby:db/fixtures/test/○○.rb
User.seed(:id,
  { id: 1,
    name: 'User',
    email: 'user@user.jp',
    password: 'password',
    password_confirmation: 'password',
  },
)
```

こんな感じでspec実行前に挿入したいseedデータを、db/fixtures/test配下に用意しておいて、

```ruby:spec/spec_helper.rb
RSpec.configure do |config|
  config.before :suite do
    fixture_paths = "#{Rails.root}/db/fixtures/test"
    SeedFu.seed(fixture_paths)
   end
end
```

こんな感じで、テスト実行前にデータを挿入してます。

ちなみにこの`SeedFu.seed`の第二引数にはフィルターが渡せるので、特定のファイルのみ実行したい場合はフィルターを追加してあげてください。





## ファイルアップロード

テスト時は`/spec/test_uploads`配下に画像をアップロードする設定を追加
(spec/test_uploads配下の指定は適当でOK)

##### Paperclipを使用する場合

```ruby:config/environments/test.rb
Paperclip::Attachment.default_options[:path] = \
 "#{ Rails.root }/spec/test_uploads/:class/:style.:extention"
```

##### Carrierwaveを使用する場合

```ruby:app/uploaders/◯◯_uploader.rb
def store_dir
 if Rails.env.test?
  "spec/test_uploads/#{model.class.to_s.underscore}
 else
  "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
 end
end
```

テストの実行が終わったらアップロードされたファイルを削除する処理を追加。

```ruby:spec/rails_helper.rb
RSpec.configure do |config|
 config.after(:suite) do
  FileUtils.rm_rf(Dir["#{ Rails.root }/spec/test_uploads/"])
 end
end
```

gitignoreに、テスト時にアップロードされたファイルを無視する設定を追加

```bash
:.gitignore
/spec/test_uploads
```



## 各種specファイルの生成
ジェネレーター使用時は不要なテストファイルが作られないように設定
(全部自動で作成されてしまうとファイルが増えてごちゃごちゃするのであまり好きじゃない)

```ruby:config/application.rb
config.generators do |g|
  g.test_framework :rspec,
    fixtures: true,
    view_specs: false,
    helper_specs: false,
    routing_specs: false,
    controller_specs: false,
    request_specs: false
  g.fixture_replacement :factory_bot, dir: "spec/factories"
end
```

必要になったらジェネレーター経由でテストファイルを作成

```bash:model_specの作成
$ rails g rspec:model ◯◯
```

```bash:factoryの作成
$ rails g factory_bot:model ◯◯
```

```bash:feature_specの作成
$ rails g rspec:feature ◯◯
```

```bash:request_specの作成
$ rails g rspec:request ◯◯
```





# 初期設定一覧
最後に、上記の設定をひとまとめにします。
ごちゃごちゃしてややこしい！　 という方は、何も考えず下記をコピーすればとりあえずOKです。

```ruby:config/application.rb
config.generators do |g|
  g.test_framework :rspec,
    fixtures: true,
    view_specs: false,
    helper_specs: false,
    routing_specs: false,
    controller_specs: false,
    request_specs: false
  g.fixture_replacement :factory_bot, dir: "spec/factories"
end
```

```bash:.rspec
--color
--require spec_helper
--format documentation
```

```ruby:spec/rails_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?

require 'simplecov'
require 'capybara/rspec'
require 'factory_bot'
require 'shoulda-matchers'
require 'vcr'
require 'spec_helper'
require 'rspec/rails'


# テスト実行前に未実行のmigrationファイルを検知して実行する
begin
  ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
  puts e.to_s.strip
  exit 1
end

# support/config配下のファイルを読み込み
Dir[Rails.root.join("spec/support/config/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/helper/*.rb")].each { |f| require f }

RSpec.configure do |config|

  # focusタグがあればそれだけ実行、なければ全spec実行
  config.filter_run focus: true
  config.run_all_when_everything_filtered = true

  # ロードするfixtureのパスを指定
  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  # ディレクトリ構成によってspec typeを自動判別する設定
  config.infer_spec_type_from_file_location!

  # spec実行後のbacktrace表示を簡素化
  config.filter_rails_from_backtrace!
end
```

```spec/spec_helper.rb
require 'simplecov'

SimpleCov.start 'rails'

RSpec.configure do |config|
  # focusタグがあればそれだけ実行、なければ全spec実行
  config.filter_run focus: true
  config.run_all_when_everything_filtered = true

  # specをランダム実行
  config.order = :random
end
```

```ruby:spec/support/config/factory_bot.rb
RSpec.configure do |config|
  # FactoryBotの呼び出し簡略化
  config.include FactoryBot::Syntax::Methods

  # springが原因でfactoryが正しく読み込まれないことを防ぐ
  config.before :all do
    FactoryBot.reload
  end
end
```

```ruby:spec/support/config/capybara.rb
RSpec.configure do |config|
 config.before(:each, type: :system) do
  driven_by :rack_test
 end

 config.before(:each, type: :system, js: true) do
  driven_by :selenium_chrome_headless
 end
end
```

```ruby:spec/spoort/config/database.rb
RSpec.configure do |config|
  # database_cleanerの設定
  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
  end
  config.before(:each) do
    DatabaseCleaner.start
  end
  config.after(:each) do
    DatabaseCleaner.clean
  end
end
```

```ruby:spec/support/config/file_upload.rb
RSpec.configure do |config|
  # テスト実行後にアップロードされたファイルを削除
  config.after(:suite) do
    FileUtils.rm_rf(Dir["#{ Rails.root }/spec/test_uploads/"])
  end
end
```

```ruby:spec/support/shoulda_matcher.rb
# shoulda-mathcerの設定
Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end
```


```ruby:spec/support/config/vcr.rb
VCR.configure do |config|
  config.cassette_library_dir = "#{ ::Rails.root }/spec/cassettes"
  config.hook_into :webmock
  config.ignore_localhost = true
  config.configure_rspec_metadata!
end
```

# 最後に

「こんな設定あるよー」とか「これ便利だよー」みたいなのあれば、教えてほしいです！