【问题标题】:Testing creation of associated objects on callback with Rspec on Rails使用 Rspec on Rails 在回调中测试关联对象的创建
【发布时间】:2011-10-26 20:26:54
【问题描述】:

试图围绕 rspec 和适当的测试,并且很难正确地执行以下操作

假设我们有三个类

Class User
    belongs_to :company
    has_and_belongs_to_many :roles
end

Class Company
    has_many :users
end

Class Role
    has_and_belongs_to_many :users
end

在 User 类中,如果用户是第一个与公司关联的用户,我有 before_create 回调分配用户默认的“company_admin”角色

def check_for_initial_company_admin_role
  if self.company.users.count == 0
    self.roles << Role.find_by_name("company_admin")
  end
end

如果用户是与公司关联的第一个用户,如何在我的模型规范中正确测试该用户被分配了“company_admin”角色?


更新 解决方案

describe "#check_for_initial_company_admin_role" do
  it "sets the first user created to be the administrator" do
    Factory(:role)
    user = Factory(:user)

    user.roles.count.should be > 0
    user.roles.should include Role.find_by_name("company_admin")
  end
end


Factory.define :user do |f|
  f.email { Factory.next(:email) }
  f.password "secret"
  f.password_confirmation "secret"
  f.association :company
end 

Factory.define :role do |f|
  f.name "company_admin"
end

Factory.define :company do |f|
  f.name "foobar"
  f.vat_id "1234"
end

【问题讨论】:

    标签: ruby-on-rails rspec


    【解决方案1】:

    我会这样处理它:

    describe "#check_for_initial_company_admin_role" do
      it "sets the first user created to be the administrator" do
        company = Factory(:company)
        user = Factory(:user)
        company.users << user
    
    
        user.roles.count.should > 0
        user.roles.should include Role.find_by_name("company_admin")
      end
    end
    

    这里可能不正确的假设是您在测试框架中使用了 Factory Girl。如果没有,那并不会真正改变这个测试的“内容”……只是你创建公司和用户的第一行。

    您还可以选择从公司方面检查用户,但老实说,这感觉就像一个完全不同的测试——测试这些模型之间的关联。

    我会采取的方法是,由于这实际上是一个模型测试,您需要创建和更改真实的模型对象,而不是模拟这些对象。如果这是一个控制器测试,我会模拟模型并积极地存根模型。

    我希望这有助于解决您的问题。如果没有,请告诉我我在哪里,我会再做一遍:) 我只有大约一年的时间进入 rspec,但我发现一旦我开始思考如何测试模型与模型。控制器我已经爱上它了。

    【讨论】:

    • OT:使用'>'时请使用'be':user.roles.count.should be &gt; 0
    • 也就是说,您不需要那种期望,因为 user.roles.should include(...) 暗示了它。否则,这与我将采用的方法相同,但我可能会先编写规范;)
    • 啊,好点子。我经常对直接布尔关系的期望感到“困惑”。我会将其添加到我的最佳实践内部列表中。谢谢!是的,今年我学会了编写规范,让它失败,实现代码以使其绿色:) 真的接受了这种方法。
    • 我认为这个例子实际上行不通。它是在将用户分配给公司之前创建用户。因此,公司将是 nil,并且在 nilClass 上调用 users 时回调会爆炸。
    • 谢谢,让我走上正轨。我正在使用 FG,并且我没有一次性建立用户-公司关联,因此 before_save 上的用户类回调将失败,因为不存在公司对象。我更新了问题以显示有效的解决方案
    【解决方案2】:

    在不更改现有逻辑的情况下,我将在 user_spec 中测试此逻辑,如下所示:

    describe User do
      let!(:admin_role) { Role.create!(name: 'company_admin') }
      let!(:company) { Company.create! }
    
      it 'should be added to the default role when created' do
        user = company.users.create!(name: 'Joe', email: 'joe@email.com')
    
        user.should have(1).roles
        user.roles.first.should == admin_role
      end
    end
    

    注意:我会使用 FactoryGirl 之类的东西作为管理员角色和公司对象,以使它们可重用。

    您使用角色名称来指示行为并不理想。它可能会导致整个应用程序中出现大量分散的逻辑,您可以通过角色名称查找角色并使用 if/else 或 case 语句检查名称。我建议使用 Rail 的单表继承并将所有管理员角色逻辑移至单独的类。它将保持模型的逻辑更清晰,并使测试更容易。

    【讨论】:

    • 谢谢。使用角色作为单独实体的原因在于使用 Devise/CanCan 来控制身份验证/授权任务。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-10
    相关资源
    最近更新 更多