Rspecでビヘイビア(振舞)駆動開発をしよう。そこでRspecをインストールしRspecでモデルのテストをしてみます。
登録されたユーザーはログインができます。そのログインユーザーには権限があり、その権限によってできること/できないことがあることを想定しています。そこでユーザー権限「Role」を作ります。
今回は以下の流れになります。
rspec-rails、database_cleaner、factory_girl_railsのインストールと設定
ユーザ権限Roleモデルを作成
FactoryGirlsでログインユーザー(管理者と一般ユーザー)の権限(Role)を作成
Roleモデルをテスト
Rspec をインストール
テストデータ(だけに限定されるわけではありませんが)を登録するのに便利な gem「factory_girl_rails」と、テストを実行後データベースを初期状態に戻してくれる gem「database_cleaner」もインストールします。
Gemfile
File: Gemfile
1
2
3
4
5
group :development, :test do
gem 'rspec-rails', '~> 3.2.1'
gem 'database_cleaner'
gem 'factory_girl_rails'
end
バンドルインストールします。
1
[rails_app]$ bundle install
インストールコマンド
Rspec のインストールには次のコマンドを実行します。
1
[rails_app]$ rails generate rspec:install
その結果、「spec/」ディレクトリーが作られ、設定用の次のファイルが追加されます。
- .rspec
- spec/spec_helper.rb
- spec/rails_helper.rb
spec/rails_helper.rb
インストールしたgemを利用できるようにrequireし、下部のconfigureの中に追加記入します。
File: spec/rails_helper.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'factory_girl'
require 'database_cleaner'
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
DatabaseCleanerは毎回テストを実行するたびにデータベースを初期化するようにしました。
モデル、ファクトリーの定義を作成
作成するのはユーザーの権限を表す「Role」ですが、モデルだけ作成します。
Roleモデルの作成
ここで実行するコマンドを一覧しておきます。
- rails generate model role role_name:string role_display_name:string
- rake db:migrate
- rspec spec
実行して確かめていきます。
1
[rails_app]$ rails generate model role role_name:string role_display_name:string
以下のように、モデルだけでなくRspecやFactoryGirlsで必要なファイルも作成されました。
1
2
3
4
5
6
7
invoke active_record
create db/migrate/20150530023253_create_roles.rb
create app/models/role.rb
invoke rspec
create spec/models/role_spec.rb
invoke factory_girl
create spec/factories/roles.rb
できたファイルを確認してみましょう。
File: app/models/role.rb
1
2
class Role < ActiveRecord::Base
end
File: spec/models/role_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require 'rails_helper'
RSpec.describe Role, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
```ruby
File: spec/factories/roles.rb
{: .filename}
```ruby
FactoryGirl.define do
factory :role do
role_name "MyString"
role_display_name "MyString"
end
end
rake db:migrate
でデータベースを初期化します。
1
2
3
4
5
[rails_app]$ rake db:migrate
== 20150530023253 CreateRoles: migrating ======================================
-- create_table(:roles)
-> 0.0007s
== 20150530023253 CreateRoles: migrated (0.0007s) =============================
この状態でRspecを実行してみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[rails_app]$ rspec spec --format doc
Role
add some examples to (or delete) /path/to/rails_app/spec/models/role_spec.rb (PENDING: Not yet implemented)
Pending: (Failures listed here are expected and do not affect your suite's status)
1) Role add some examples to (or delete) /path/to/rails_app/spec/models/role_spec.rb
# Not yet implemented
# ./spec/models/role_spec.rb:4
Finished in 0.02596 seconds (files took 1.39 seconds to load)
1 example, 0 failures, 1 pending
role_spec.rbのpendingの行が実行、表示されています。後ほどこのファイルを修正変更していきます。
ファクトリーの定義
ここではFactoryGirlsで管理者権限と一般ユーザー権限を作成できるように spec/factories/roles.rb を編集します。レコードは一つずつできればよいのでfind_or_create_by
を使っています。
File: spec/factories/roles.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FactoryGirl.define do
factory :role_admin, class: Role do |r|
r.role_name "admin"
r.role_display_name "管理者"
r.initialize_with { Role.find_or_create_by(role_name: role_name) }
end
factory :role_user, class: Role do |r|
r.role_name "general_user"
r.role_display_name "一般ユーザー"
r.initialize_with { Role.find_or_create_by(role_name: role_name) }
end
end
Roleモデルのテスト
仕様を決める
では、spec/models/role_spec.rb の編集を始めます。まずユーザー権限の仕様を決めていきましょう。
File: spec/models/role_spec.rb
1
2
3
4
5
6
7
8
9
10
11
require 'rails_helper'
RSpec.describe Role, type: :model do
context "権限" do
it "管理者権限がある。"
it "一般ユーザー権限がある。"
it "各権限は、データベースに1つずつしか作られない。"
end
end
ブロックを付けずにit
だけで仕様を記述すると見やすいですね。
ではRspecを実行してみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[rails_app]$ rspec spec --format doc
Role
権限
管理者権限がある。 (PENDING: Not yet implemented)
一般ユーザー権限がある。 (PENDING: Not yet implemented)
各権限は、データベースに1つずつしか作られない。 (PENDING: Not yet implemented)
Pending: (Failures listed here are expected and do not affect your suite's status)
1) Role 権限 管理者権限がある。
# Not yet implemented
# ./spec/models/role_spec.rb:6
2) Role 権限 一般ユーザー権限がある。
# Not yet implemented
# ./spec/models/role_spec.rb:7
3) Role 権限 各権限は、データベースに1つずつしか作られない。
# Not yet implemented
# ./spec/models/role_spec.rb:8
Finished in 0.02748 seconds (files took 1.4 seconds to load)
3 examples, 0 failures, 3 pending
このようにブロックなしでit
を使うとペンディング扱いとなります。3個のテストがあり、失敗が0個でペンディングが3個と表示されています。
テストを実装する
管理者権限を作成し、データベースに正しく登録されたことを確認するテストを作ります。
FactoryGirls 先ほど定義したファクトリーのうちファクトリー名が「:role_admin」の権限を作るには
1
FactoryGirl.create(:role_admin)
のように
create
メソッドを使うことでインスタンスを作成すると同時にデータベースへも登録されます。インスタンスだけ作成しデータベースへ登録しない時には、build
メソッドを使います。1
FactoryGirl.build(:role_admin)
Rspecのexpectメソッド 登録された値が、期待している値と等しいことをテストするには、
1
expect(受け取った値).to eq(期待する値)
のように記述し、その否定のときには
.to_not
や.not_to
メソッドを使います。1
expect(受け取った値).not_to eq(受け取ってはいけない値)
と記述します(.to_notでもよい)。また、
eq
の部分は「マッチャー(matcher)」といい、be_truthy
やbe_falsey
のようないろいろなマッチャーがあります(今回はマッチャーについてはふれません)。
File: spec/models/role_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'rails_helper'
RSpec.describe Role, type: :model do
context "権限" do
it "管理者権限がある。" do
role_admin = FactoryGirl.create(:role_admin)
role = Role.all
expect(role.size).to eq(1)
expect(role[0].role_name).to eq("admin")
expect(role[0].role_display_name).to eq("管理者")
expect(role[0]).to eq(role_admin)
end
it "一般ユーザー権限がある。"
it "各権限は、データベースに1つずつしか作られない。"
end
end
テスト事項は
- Roleテーブルに登録されているのは1件である
- 登録された「role_name」は「admin」である
- 登録された「role_display_name」は「管理者」である
としました。ついでに登録したインスタンスと取得したActiveRecordが等しいこともテストしています。
rspecを実行します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[rails_app]$ rspec spec --format doc
Role
権限
管理者権限がある。
一般ユーザー権限がある。 (PENDING: Not yet implemented)
各権限は、データベースに1つずつしか作られない。 (PENDING: Not yet implemented)
Pending: (Failures listed here are expected and do not affect your suite's status)
1) Role 権限 一般ユーザー権限がある。
# Not yet implemented
# ./spec/models/role_spec.rb:17
2) Role 権限 各権限は、データベースに1つずつしか作られない。
# Not yet implemented
# ./spec/models/role_spec.rb:18
Finished in 0.04412 seconds (files took 1.44 seconds to load)
3 examples, 0 failures, 2 pending
無事、作成したテストは成功しました。
端末では、成功したテストは緑色にカラー表示され、失敗した時には赤色で表示されます。3個のテストのうち失敗は0個、2個がペンディングと表示されています。
テストを完成させ実行します。
File: spec/models/role_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
require 'rails_helper'
RSpec.describe Role, type: :model do
context "権限" do
it "管理者権限がある。" do
role_admin = FactoryGirl.create(:role_admin)
role = Role.all
expect(role.size).to eq(1)
expect(role[0].role_name).to eq("admin")
expect(role[0].role_display_name).to eq("管理者")
expect(role[0]).to eq(role_admin)
end
it "一般ユーザー権限がある。" do
role_user = FactoryGirl.create(:role_user)
role = Role.all
expect(role.size).to eq(1)
expect(role[0].role_name).to eq("general_user")
expect(role[0].role_display_name).to eq("一般ユーザー")
expect(role[0]).to eq(role_user)
end
it "各権限は、データベースに1つずつしか作られない。" do
role_admin = FactoryGirl.create(:role_admin)
role_user = FactoryGirl.create(:role_user)
role = Role.all
expect(role.size).to eq(2)
FactoryGirl.create(:role_admin)
FactoryGirl.create(:role_user)
role.reload
expect(role.size).to eq(2)
expect(role[0]).to eq(role_admin)
expect(role[1]).to eq(role_user)
expect(role[2]).to eq(nil)
end
end
end
結果です。
1
2
3
4
5
6
7
8
9
10
11
[rails_app]$ rspec spec --format doc
Role
権限
管理者権限がある。
一般ユーザー権限がある。
各権限は、データベースに1つずつしか作られない。
Finished in 0.09157 seconds (files took 1.38 seconds to load)
3 examples, 0 failures
すべて成功し緑色になりました。
今回はテストの基本まででした。
次回にもう一度モデルのテストをより実践的に行います。