使用factory_girl创建的模型实例在构建和创建之间具有属性更改

时间:2014-05-21 04:43:51

标签: ruby-on-rails rspec ruby-on-rails-3.2 factory-bot

我在我的rspec测试中使用factory_girl。我遇到了在构建中正确设置creator_id的问题,然后更改为在上一个测试中创建的模型的id。具有来自先前测试的id的模型显然不存在,因此测试失败。我不确定我做错了什么。

我的User工厂的一部分:

factory :unconfirmed_user, class: User do
    sequence(:user_name) { |n| "user#{n}" }
    sequence(:email) { |n| "user#{n}@example.com" }
    sequence(:authentication_token) { |n| "some random token #{n}" }
    sequence(:password) { |n| "password #{n}" }

    roles_mask { 2 } # user role

    trait :confirmed do
      after(:create) do |user|
        user.skip_confirmation_notification!
        #user.confirm!
        user.skip_confirmation!

        user.confirmation_token = user.class.confirmation_token
        user.confirmation_sent_at = Time.now.utc
        user.save(:validate => false)

        user.ensure_authentication_token!
      end
    end
    ....
    factory :confirmed_user, traits: [:confirmed], aliases: [:user, :creator, :updater, :deleter]
    ....
end

日志记录完成如下:

FactoryGirl.define do

  after(:build) { |object|
    is_blank = object.respond_to?(:creator) && object.creator.blank? && !object.is_a?(User)
    Rails.logger.warn "Built #{is_blank ? '[blank]' : ''} [#{object.object_id}] #{object.inspect}"
  }
  after(:create) { |object|
    is_blank = object.respond_to?(:creator) && object.creator.blank? && !object.is_a?(User)
    Rails.logger.warn "Created #{is_blank ? '[blank]' : ''} [#{object.object_id}] #{object.inspect}"
  }

....

这是记录的内容。 Project是一个模型,其属性creator_idUser

Built  [82788672] #<Project id: nil, name: "project14", description: "project description 14", urn: "urn:project:14", notes: "note number 14", creator_id: 100, updater_id: nil, deleter_id: nil, deleted_at: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, created_at: nil, updated_at: nil>
Created [blank] [82788672] #<Project id: 11, name: "project14", description: "project description 14", urn: "urn:project:14", notes: "note number 14", creator_id: 99, updater_id: 99, deleter_id: nil, deleted_at: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, created_at: "2014-05-21 04:08:30", updated_at: "2014-05-21 04:08:30">

请注意creator_id100中以build开头,然后更改为99中的created。为什么会这样?

更新

User模型的相关部分:

require 'role_model'

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :token_authenticatable, :confirmable, :lockable, :timeoutable

  include RoleModel

  attr_accessible :user_name, :email, :password, :password_confirmation, :remember_me,
                  :roles, :roles_mask, :preferences,
                  :image

  roles :admin, :user, :harvester 

  model_stamper

  # relations
  has_many :accessible_projects, through: :permissions, source: :project
  has_many :readable_projects, through: :permissions, source: :project, conditions: 'permissions.level = reader'
  has_many :writable_projects, through: :permissions, source: :project, conditions: 'permissions.level = writer'

  has_many :created_projects, class_name: 'Project', foreign_key: :creator_id, inverse_of: :creator
  has_many :updated_projects, class_name: 'Project', foreign_key: :updater_id, inverse_of: :updater
  has_many :deleted_projects, class_name: 'Project', foreign_key: :deleter_id, inverse_of: :deleter

  # validations
  validates :user_name, presence: true, uniqueness: {case_sensitive: false}
  validates :email, presence: true, uniqueness: true
  validates :roles_mask, presence: true
  validates_attachment_content_type :image, content_type: /^image\/(jpg|jpeg|pjpeg|png|x-png|gif)$/, message: 'file type %{value} is not allowed (only jpeg/png/gif images)'

  before_validation :ensure_user_role

  def projects
    (self.created_projects + self.accessible_projects).uniq
  end

  private
  def ensure_user_role
    self.roles << :user if roles_mask.blank?
  end

1 个答案:

答案 0 :(得分:1)

我终于弄明白了。拼图的关键部分是&#34;在发生异常时不调用过滤器&#34; (在Rails APIDock页面的评论中找到,目前无法找到确切的链接。)

userstamp gem I am using sets the user on a before_filter, then removes the user in an after_filter。问题是如果有异常,则after_filter将不会运行。这在正常操作中无关紧要 - 用户仍将被设置,但在其他用户登录时将被覆盖。这可能会导致其他我不知道的安全问题。

测试中的问题是,因为模型是在通常的rails应用程序周期之外创建并插入到db中的,所以如果发生异常则由userstamp gem存储的id在测试之间保持(因为它们被存储)在线程中)。这意味着标记了错误的用户标识(这就是在构建和创建之间id发生变化的原因)。

总而言之,rails before_filter和after_filter的工作方式不同,导致混乱。测试会导致gem中的活动不同,在这种情况下会导致状态保持在测试之间。

我似乎已经通过在处理错误的方法的末尾添加调用来重置/清除存储的用户来解决它(在我的情况下,在application_controller.rb中)。这确保了在操作结束时,就好像after_filter一样,所以userstamp gem的状态就像它期望的那样。我可能需要使用不同的(并且更积极维护的)gem或者gem的分支:/

呼。