Request SpecsでWebAPIのCRUDテスト例

以前、Rspecの記事を書きましたがController Specsを利用していました。今回はRequest Specsを利用します。この記事では、RailsをAPIモードで作成した後の状態からRspecを導入して、参照・登録・更新・削除するAPIのテストコードを書くところまで行きます。

テストコードは冗長な部分がありますが、できるだけテストの流れが分かりやすく見えるようなメソッドを選んでいます。
テスト実施したソースコードは以下をご覧ください。
[GitHub - rspec_api](https://github.com/kaikusakari/rspec_api)

本記事は、以下の公式の導入手順に沿って進めています。
[GitHub - rspec/rspec-rails: RSpec for Rails-3+](https://github.com/rspec/rspec-rails)

## 対象バージョン
**ruby**
 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]

**Gem**

| 名前 | バージョン |
|-----------:|------------:|
| Rails | 5.0.2 |
| Rspec | 3.5.4 |
| rspec-rails | 3.5.2 |
| factory_girl_rails | 4.8.0 |

## 環境の構築
テストを実施する上で必要な環境を整えます。
まずはプロジェクトと、テスト対象のコントローラ・DBを作成します。

```shell-session
$ rails new app --api
$ rails generate scaffold User name:string mail:string
$ rake db:migrate
$ rake db:migrate RAILS_ENV=test
$ ls -l app/db/
test.sqlite3
```

必要なGemfileを編集

```rb:Gemfile
~略~
group :development, :test do
  gem 'rspec-rails'
  gem 'factory_girl_rails'
end
```

インストール実施

```shell-session
$ bundle install
$ rails generate rspec:install
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

$ bundle exec rspec
No examples found.
Finished in 0.00065 seconds (files took 0.20726 seconds to load)
0 examples, 0 failures
```

環境を準備したので、APIのテストコードを作成します。
（今回はGemの通り、factory_girlを利用します）

## テスト対象API
対象APIは以下の通り

|NO| method | path | proxy |
|:--:|:-----------|:------------|:------------|
|1|GET    | /users/:id(.:format) | users#show
|2|GET    | /users(.:format) | users#index
|3|POST   | /users(.:format)|users#create
|4|PUT    | /users/:id(.:format) | users#update
|5|DELETE |/users/:id(.:format) | users#destroy


## 1　GET /users/:id(.:format)
一つのレコードを参照するAPIを確認します。
**FG**

```rb:app/spec/factories/users.rb
FactoryGirl.define do
  factory :users_show, class: User do
    name "name_act1"
    mail "mail_act1@example.com"
  end
end
```

**Rspec**

```rb:app/spec/requests/users_spec.rb
require 'rails_helper'

RSpec.describe "Users", type: :request do
  describe "GET /users/:id" do
    before do
      @user = FactoryGirl.create(:users_show)
    end
    it "res code 200" do
      get "/users" + "/" + @user[:id].to_s
      expect(response.status).to eq 200
      json = JSON.parse(response.body)
      expect(@user.name).to eq json['name']
    end
  end
end
```
## 2　GET /users/(.:format)
テーブルの全レコードを参照するAPIを確認します。
**FG**

```rb:app/spec/factories/users.rb
FactoryGirl.define do
  factory :users_index_1, class: User do
    name "name_act2"
    mail "mail_act2@example.com"
  end
  factory :users_index_2, class: User do
    name "name_act2"
    mail "mail_act2@example.com"
  end
end
```

**Rspec**

```rb:app/spec/requests/users_spec.rb
require 'rails_helper'

RSpec.describe "Users", type: :request do
  describe "GET /users" do
    before do
      FactoryGirl.create(:users_index_1)
      FactoryGirl.create(:users_index_2)
    end
    it "res code 200" do
      get "/users"
      expect(response.status).to eq 200
      json = JSON.parse(response.body)
      expect(User.count).to eq json.count
    end
  end
end
```

## 3　POST /users(.:format)
1レコードを登録するAPIを確認します。
**FG**

```rb:app/spec/factories/users.rb
FactoryGirl.define do
  factory :users_create, class: User do
    name "name_act3"
    mail "mail_act3@example.com"
  end
end
```

**Rspec**

```rb:app/spec/requests/users_spec.rb
require 'rails_helper'
  describe "POST /users" do
    before do
      @user = FactoryGirl.attributes_for(:users_create)
    end
    it "res code 200" do
      post "/users", :user => @user, :name => @user[:name], :mail => @user[:mail]
      expect(response.status).to eq 201
      json = JSON.parse(response.body)
      expect(@user[:name]).to eq json['name']
    end
  end
RSpec.describe "Users", type: :request do
end
```


## 4　PUT /users/:id(.:format)
1レコードを更新するAPIを確認します。
**FG**

```rb:app/spec/factories/users.rb
FactoryGirl.define do
  factory :users_update_before, class: User do
    name "name_act4"
    mail "mail_act4@example.com"
  end
  factory :users_update_after, class: User do
    name "name_act4_after"
    mail "mail_act4_after@example.com"
  end
end
```

**Rspec**

```rb:app/spec/requests/users_spec.rb
  describe "PUT /users/:id" do
    before do
      @user_before = FactoryGirl.create(:users_update_before)
      @user = FactoryGirl.attributes_for(:users_update_after)
    end
    it "res code 200" do
      put "/users" + "/" + @user_before.id.to_s, :user => @user, :name => @user[:name], :mail => @user[:mail]
      expect(response.status).to eq 200
      json = JSON.parse(response.body)
      expect(@user[:name]).to eq json['name']
    end
  end
```

## 5　DELETE /users/:id(.:format)
1レコードを削除するAPIを確認します。
**FG**

```rb:app/spec/factories/users.rb
FactoryGirl.define do
  factory :users_delete, class: User do
    name "name_act5"
    mail "mail_act5@example.com"
  end
end
```

**Rspec**

```rb:app/spec/requests/users_spec.rb
  describe "DELETE /users/:id" do
    before do
      @user = FactoryGirl.create(:users_delete)
    end
    it "res code 204" do
      expect {
	delete "/users" + "/" + @user.id.to_s
      }.to change{ User.count }.by(-1)
      expect(response.status).to eq 204
    end
  end
end
```