属性のアクセシビリティ テストに関する別の StackOverflow の質問に答えてshoulda-matchersについて学習した後(そして、それらは非常に素晴らしいと思いました)、Rails チュートリアルで行ったモデル テストをリファクタリングして、より簡潔で完全なものにすることを試みることにしました。これを行ったのは、モジュールとモジュールのドキュメントからのインスピレーションと、モデル内のテストの構造化に関するこの StackOverflow の回答のおかげです。しかし、まだよくわからない点がいくつかあり、これらのテストをどのように改善できるかを考えています。 Shoulda::Matchers::ActiveRecord
Shoulda::Matchers::ActiveModel
Rails チュートリアルのユーザー仕様を例として使用します。これは最も詳細であり、改善できる多くの領域をカバーしています。次のコード例は、元のuser_spec.rbから変更されており、行までコードを置き換えていますdescribe "micropost associations"
。仕様はuser.rbモデルに対してテストされ、そのファクトリは factory.rb で定義されます。
仕様/モデル/user_spec.rb
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# password_digest :string(255)
# remember_token :string(255)
# admin :boolean default(FALSE)
#
# Indexes
#
# index_users_on_email (email) UNIQUE
# index_users_on_remember_token (remember_token)
#
require 'spec_helper'
describe User do
let(:user) { FactoryGirl.create(:user) }
subject { user }
describe "database schema" do
it { should have_db_column(:id).of_type(:integer)
.with_options(null: false) }
it { should have_db_column(:name).of_type(:string) }
it { should have_db_column(:email).of_type(:string) }
it { should have_db_column(:created_at).of_type(:datetime)
.with_options(null: false) }
it { should have_db_column(:updated_at).of_type(:datetime)
.with_options(null: false) }
it { should have_db_column(:password_digest).of_type(:string) }
it { should have_db_column(:remember_token).of_type(:string) }
it { should have_db_column(:admin).of_type(:boolean)
.with_options(default: false) }
it { should have_db_index(:email).unique(true) }
it { should have_db_index(:remember_token) }
end
describe "associations" do
it { should have_many(:microposts).dependent(:destroy) }
it { should have_many(:relationships).dependent(:destroy) }
it { should have_many(:followed_users).through(:relationships) }
it { should have_many(:reverse_relationships).class_name("Relationship")
.dependent(:destroy) }
it { should have_many(:followers).through(:reverse_relationships) }
end
describe "model attributes" do
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:remember_token) }
it { should respond_to(:admin) }
it { should respond_to(:microposts) }
it { should respond_to(:relationships) }
it { should respond_to(:followed_users) }
it { should respond_to(:reverse_relationships) }
it { should respond_to(:followers) }
end
describe "virtual attributes and methods from has_secure_password" do
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:authenticate) }
end
describe "accessible attributes" do
it { should_not allow_mass_assignment_of(:password_digest) }
it { should_not allow_mass_assignment_of(:remember_token) }
it { should_not allow_mass_assignment_of(:admin) }
end
describe "instance methods" do
it { should respond_to(:feed) }
it { should respond_to(:following?) }
it { should respond_to(:follow!) }
it { should respond_to(:unfollow!) }
end
describe "initial state" do
it { should be_valid }
it { should_not be_admin }
its(:remember_token) { should_not be_blank }
its(:email) { should_not =~ /\p{Upper}/ }
end
describe "validations" do
context "for name" do
it { should validate_presence_of(:name) }
it { should_not allow_value(" ").for(:name) }
it { should ensure_length_of(:name).is_at_most(50) }
end
context "for email" do
it { should validate_presence_of(:email) }
it { should_not allow_value(" ").for(:email) }
it { should validate_uniqueness_of(:email).case_insensitive }
context "when email format is invalid" do
addresses = %w[user@foo,com user_at_foo.org example.user@foo.]
addresses.each do |invalid_address|
it { should_not allow_value(invalid_address).for(:email) }
end
end
context "when email format is valid" do
addresses = %w[user@foo.COM A_US-ER@f.b.org frst.lst@foo.jp a+b@baz.cn]
addresses.each do |valid_address|
it { should allow_value(valid_address).for(:email) }
end
end
end
context "for password" do
it { should ensure_length_of(:password).is_at_least(6) }
it { should_not allow_value(" ").for(:password) }
context "when password doesn't match confirmation" do
it { should_not allow_value("mismatch").for(:password) }
end
end
context "for password_confirmation" do
it { should validate_presence_of(:password_confirmation) }
end
end
# ...
end
これらのテストに関する具体的な質問:
- データベーススキーマをテストする価値はありますか? 上記の StackOverflow の回答のコメントには、「動作に関連するものだけをテストし、列の存在やインデックスの動作は考慮していません。誰かが意図的に削除しない限り、データベースの列は消えるだけではありませんが、コードレビューと信頼でそれを防ぐことができます」と私は同意しますが、データベーススキーマの構造がテストされ、
Shoulda::Matchers::ActiveRecord
モジュールの存在が正当化される正当な理由はあり ますか? おそらく、重要なインデックスだけをテストする価値があります...? - 下のテストは、
should have_many
下の対応するテストを"associations"
置き換えますか? テストがモデル ファイル内の関連する宣言を検索するだけなのか、それとも実際に と同じ機能を実行するのかはわかりません。should respond_to
"model attributes"
should have_many
has_many
should respond_to
- コンテンツと構造の両方で、これらのテストをより簡潔/読みやすく/完全にするために、他にコメント/提案はありますか?