gpt4 book ai didi

ruby-on-rails - 使用 shoulda 重构 Rails 模型上的 rspec 测试

转载 作者:行者123 更新时间:2023-12-04 02:21:33 26 4
gpt4 key购买 nike

了解后shoulda-matchers通过回答 another StackOverflow question on attribute accessibility tests (并且认为它们非常棒),我决定尝试重构我在 The Rails Tutorial 中所做的模型测试试图使它们更加简洁和彻底。得益于模块文档 Shoulda::Matchers::ActiveRecord 的一些启发,我做到了这一点。和 Shoulda::Matchers::ActiveModel ,以及 this StackOverflow answer关于在模型中构建应该测试。但是,仍有一些事情我不确定,我想知道如何才能使这些测试变得更好。

我将使用 Rails 教程中的用户规范作为我的示例,因为它是最详细的,并且涵盖了许多可以改进的领域。以下代码示例已从原始 user_spec.rb 更改。 , 并替换代码直到 describe "micropost associations"线。该规范针对 user.rb 进行测试型号,其工厂定义在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[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 answer mentioned above 中的评论说“我只测试与行为相关的东西,我不考虑列或索引行为的存在。除非有人故意删除它们,否则数据库列不会消失,但你可以通过代码审查和信任”,我同意,但是有什么正当理由可以测试数据库模式的结构,从而证明 Shoulda::Matchers::ActiveRecord 的存在是合理的。模块?也许只有重要的指标值得测试......?
  • should have_many "associations" 下的测试替换它们对应的should respond_to "model attributes" 下的测试?我不知道 should have_many测试只是寻找相关的has_many在模型文件中声明或实际执行与 should respond_to 相同的功能.
  • 您是否有任何其他意见/建议可以使这些测试在内容和结构上更简洁/可读/彻底?
  • 最佳答案

    1) Shoulda::Matchers::ActiveRecord 模块中包含的不仅仅是列和索引匹配器。我会在 included classes一点点,看看你能找到什么。这就是 have_many , belong_to等来自。不过,作为记录,我认为其中的大部分内容几乎没有值(value)。

    2) 是的,have_many 等宏测试比模型是否响应方法要多得多。来自 source code ,您可以准确地看到它正在测试什么:

    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测试并用有值(value)的测试替换它们。当有人查看您的测试时,他们应该能够理解该类的公共(public) API。当我看到您的对象对“跟随?”之类的内容做出响应时,我可以做出假设,但并不真正知道这意味着什么。需要争论吗?它是否返回 bool 值?对象是跟随某物还是某物跟随该对象?

    关于ruby-on-rails - 使用 shoulda 重构 Rails 模型上的 rspec 测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12031265/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com