【问题标题】:RSpec uniqueness email test fails with FactoryGirlFactoryGirl 的 RSpec 唯一性电子邮件测试失败
【发布时间】:2012-07-07 20:54:10
【问题描述】:

编辑

使用问题的答案,我将测试更改为正确测试并通过的以下测试..

describe "when email is already taken" do
  let(:user_with_same_email) { @user.dup }
  before do
    user_with_same_email.email.upcase!
    user_with_same_email.save
  end

  it { user_with_same_email.should_not be_valid }
end

注意:不使用let(:user_with_same_email) { @user.dup } 会使测试失败,因为如果变量user_with_same_email 只是在before 块中重复,就像此问题的所选答案一样。


我有一个User 模型和一个user_spec.rb 测试文件,它针对User 模型属性进行了各种验证。

之前我在user_spec.rb 文件的顶部写了以下内容来测试User 模型:

describe User do

  before do
  @user = User.new(name: "Example User", email: "user@example.com",
                 password: "foobar88", password_confirmation: "foobar88")
end
...

我想将此模型创建移至FactoryGirl,因此我创建了一个factories.rb 文件:

FactoryGirl.define do
  factory :user do
    name "foo"
    email { "#{name}@example.com" }
    password "foobar99"
    password_confirmation "foobar99"
  end
end

然后我更改了我的user_spec.rb

describe User do

  before do
    @user = FactoryGirl.create(:user)
  end
...

现在每个测试都像以前一样通过,除了一个:

describe "when email is already taken" do
  before do
    user_with_same_email = @user.dup
    user_with_same_email.email = @user.email.upcase
    user_with_same_email.save
  end

  it { should_not be_valid }
end

现在除非 `FactoryGirl 跳过我的电子邮件唯一性验证,否则我无法弄清楚这里出了什么问题。

我的User模型验证码:

class User < ActiveRecord::Base

  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i unless const_defined?(:VALID_EMAIL_REGEX)

  has_secure_password
  attr_accessible :name, :email, :password, :password_confirmation

  has_many :programs

  before_save { self.email.downcase! }

  validates :name, presence: true, length: { maximum: 50 }
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }

【问题讨论】:

    标签: ruby-on-rails-3 tdd factory-bot rspec-rails


    【解决方案1】:

    问题是当你说它 { should_not be_valid } 时,RSpec 会检查主题。在这种情况下,主题是 User.new(您在顶部有“描述用户”,因此除非您指定其他内容,否则这是默认设置)。

    您想改为检查 user_with_same_email 的有效性。

    编辑: 试试这个,我认为它可能会起作用:

    describe "when email is already taken" do
      before do
        @user_with_same_email = @user.dup
        @user_with_same_email.email = @user.email.upcase
        @user_with_same_email.save
      end
    
      it { @user_with_same_email.should_not be_valid }
    end
    

    【讨论】:

    • User.new 被替换为 FactoryGirl.create(:user)
    • 我将测试代码更改为我认为可行的代码。请尝试一下。
    • 你是对的。 it 函数检查有效主题的有效性,而不是重复的无效 user_with_same_email 实例。谢谢。
    【解决方案2】:

    看起来您可能正在使用(或引用)Michael Hartl 的Rails Tutorial。这是我的代码对你正在做的事情的样子,所以我希望它可以使用:

    spec/models/user_spec.rb

    describe User do
    
      let(:user) { valid_user }
      subject { user }
    
      # ...
    
      context "when email address is already taken" do
        before { save_user(user) }
        it { should_not be_valid }
      end
    
      # ...
    end
    

    spec/support/utilities.rb(创建特定用户)

    def valid_user
      User.new(name:     "Example User", 
               email:    "user@example.com",
               password: "foobar", 
               password_confirmation: "foobar")
    end
    
    # ...
    
    def save_user(user)
      user_with_same_email = user.dup
      user_with_same_email.email.upcase!
      user_with_same_email.save
    end
    

    供参考:spec/factories.rb(仅创建任何旧的随机用户)

    FactoryGirl.define do
      factory :user do
        sequence(:name)  { |n| "Person #{n}" }
        sequence(:email) { |n| "person_#{n}@example.com" }
        password "foobar"
        password_confirmation "foobar"
    
        # ...
      end
      # ...
    end
    

    更新:在this StackOverflow answer 找到了您正在寻找的答案,概述了同样的问题。我也用我的代码对其进行了测试,它对我有用。

    更新 2:也更改了我的代码,在我想要用户但不希望将其保存到数据库中时使用 FactoryGirl.buildThis StackOverflow answer 帮助我理解了。

    spec/models/user_spec.rb

    describe User do
    
      let(:user) { FactoryGirl.create(:user) }
      subject { user }
    
      # ...
    
      context "when email address is already taken" do
        let(:user_with_same_email) do
          FactoryGirl.build(:user, email: user.email)
        end
    
        subject { user_with_same_email }
    
        before do
          user_with_same_email.email.upcase!
          user_with_same_email.save
        end
    
        it { should_not be_valid }
      end
      # ...
    end
    

    感谢您提出这个问题。让我深思熟虑,并在我自己的代码中进行一些重构。

    【讨论】:

    • 这与我的初始代码相同,只是代码已被提取到 2 个辅助函数中。我看不到您在代码中调用 FactoryGirl 的位置。
    • 在这种情况下,我不调用 FactoryGirl(我只是将其包含在上面以供参考),因为我希望特定用户在其属性中具有相同的值,用于 user_spec 中的所有其他测试。当我想凭空拉一些不起眼的用户做某事时,我只是使用 FactoryGirl。
    • 我能想到的唯一其他事情是将您的before 循环更改为let,然后专门标记一个subject,或者可能将您的it 块更改为@user.should_not be_valid
    • 结果是一样的。除电子邮件唯一性测试外,所有测试都像以前一样通过。
    • 您可以编辑您的答案以发布错误消息吗?也许尝试注释掉你的unless const_defined?(:VALID_EMAIL_REGEX)(你需要吗?)?否则,也许只是回到本书的uniqueness validation 部分,让它与那里的代码一起工作,然后再次开始定制。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-15
    • 1970-01-01
    • 2014-06-08
    相关资源
    最近更新 更多