了解后应该匹配器 https://github.com/thoughtbot/shoulda-matchers通过回答关于属性可访问性测试的另一个 StackOverflow 问题 https://stackoverflow.com/a/11849608/567863(并且认为它们非常棒),我决定尝试重构我在中所做的模型测试Rails 教程 http://ruby.railstutorial.org/试图使它们更加简洁和彻底。我这样做得益于模块文档的一些灵感Shoulda::Matchers::ActiveRecord http://rubydoc.info/github/thoughtbot/shoulda-matchers/master/frames and Shoulda::Matchers::ActiveModel http://rubydoc.info/github/thoughtbot/shoulda-matchers/master/frames, 也这个 StackOverflow 答案 https://stackoverflow.com/a/4611017/567863关于在模型中构建shoulda测试。然而,仍然有一些事情我不确定,我想知道如何才能使这些测试变得更好。
我将使用 Rails 教程中的用户规范作为示例,因为它是最详细的,并且涵盖了许多可以改进的领域。下面的代码示例在原来的基础上进行了更改用户规范.rb https://github.com/railstutorial/sample_app_2nd_ed/blob/master/spec/models/user_spec.rb,并向下替换代码,直到describe "micropost associations"
线。该规范测试针对user.rb https://github.com/railstutorial/sample_app_2nd_ed/blob/master/app/models/user.rb模型,其工厂定义在工厂.rb https://github.com/railstutorial/sample_app_2nd_ed/blob/master/spec/factories.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[[email protected] /cdn-cgi/l/email-protection [email protected] /cdn-cgi/l/email-protection [email protected] /cdn-cgi/l/email-protection [email protected] /cdn-cgi/l/email-protection]
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 答案 https://stackoverflow.com/a/4611017/567863说“我只测试与行为相关的东西,我不考虑列或索引行为的存在。数据库列不会消失,除非有人故意删除它们,但你可以通过代码审查和信任”,我同意这一点,但是是否有任何有效的理由来测试数据库模式的结构,从而证明存在的合理性
Shoulda::Matchers::ActiveRecord
模块?也许只有重要的指标才值得测试……?
- Do the
should have_many
测试下"associations"
替换它们对应的should respond_to
测试下"model attributes"
?我无法判断是否should have_many
测试只是寻找相关的has_many
模型文件中的声明或实际上执行与以下相同的功能should respond_to
.
- 您还有其他意见/建议可以使这些测试在内容和结构上更加简洁/可读/彻底吗?
1) Shoulda::Matchers::ActiveRecord 模块不仅仅包含列和索引匹配器。我会在周围挖掘包含的课程 https://github.com/thoughtbot/shoulda-matchers/tree/master/lib/shoulda/matchers/active_record一点点,看看你能找到什么。这就是have_many
, belong_to
等来自。但郑重声明,我认为其中大部分内容没有什么价值。
2)是的,宏例如have_many
测试的内容不仅仅是模型是否响应方法。来自源代码 https://github.com/thoughtbot/shoulda-matchers/blob/master/lib/shoulda/matchers/active_record/association_matcher.rb#L108-120,您可以准确地看到它正在测试什么:
def matches?(subject)
@subject = subject
association_exists? &&
macro_correct? &&
foreign_key_exists? &&
through_association_valid? &&
dependent_correct? &&
class_name_correct? &&
order_correct? &&
conditions_correct? &&
join_table_exists? &&
validate_correct?
end
3)使测试更具可读性和/或更简洁绝对是一个需要回答的主观问题。每个人都会根据他们的背景和经验给你不同的答案。我个人会摆脱所有respond_to
测试并用有价值的测试替换它们。当有人查看您的测试时,他们应该能够理解该类的公共 API。当我看到你的对象对“以下?”之类的内容做出响应时,我可以做出假设,但并不真正知道它的含义。这需要争论吗?它返回一个布尔值吗?是物体跟随某物,还是某物跟随物体?
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)