使用 Rspec on Rails 测试回调中关联对象的创建

发布于 2024-12-12 03:58:38 字数 1186 浏览 0 评论 0原文

尝试围绕 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

Trying to wrap my head around rspec and proper testing and having some hard time to do the following properly

Let's say we have three classes like

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

In the User class I have before_create callback that assign the user default 'company_admin' role, if the user is first one to be associated with the company

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

How do I properly test in my model spec that the user gets assigned the 'company_admin' role in case he's the first user associated with the company?


UPDATE
working solution

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

丿*梦醉红颜 2024-12-19 03:58:38

我会这样处理:

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 大约一年,但我发现一旦我开始思考如何测试模型与模型。我已经爱上它了。

I would approach it like this:

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

An assumption here that may be incorrect is that you are using Factory Girl in your test framework. If not, that doesn't really change the "meat" of this test...just those first lines where you create the company and user.

You could also optionally check the user from the company side of things but honestly that feels like a different test entirely--one testing the association between those models.

The approach I would take is that since this is actually a model test you need to create and alter a real model objects, rather than mocking out those objects. If this were a controller test, I'd mock the models and stub the models aggressively.

I hope this helps and addresses your question. If not, let me know where I'm off base and I'll make another pass at it :) I'm only about a year into rspec but I've found that once I wrapped my head around how to test models vs. controllers I've come to love it.

烟燃烟灭 2024-12-19 03:58:38

在不更改现有逻辑的情况下,我将在 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: '[email protected]')

    user.should have(1).roles
    user.roles.first.should == admin_role
  end
end

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

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

Without changing your existing logic, I would test this logic in the user_spec like so:

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: '[email protected]')

    user.should have(1).roles
    user.roles.first.should == admin_role
  end
end

Note: I would be using something like FactoryGirl for the admin role and company objects to make them reusable.

Your use of the role name to indicate behavior is not ideal. It will likely lead to lots of scattered logic throughout your application where you are finding the role by its name and checking the name with if/else or case statements. I would recommend using Rail's single table inheritance and moving all the admin role logic to a separate class. It will keep the model's logic cleaner and make testing much easier.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文