【问题标题】:Passing variable between multiple contexts with Rspec使用 Rspec 在多个上下文之间传递变量
【发布时间】:2017-09-05 02:11:00
【问题描述】:

我正在使用FactoryGirlRspec 编写一些测试。

spec/factories/students.rb

FactoryGirl.define do
  factory :student do
  end

  factory :student_with_profile_and_identity, class: 'Student' do
    after(:create) do |student|
      create(:profile, profileable: student)
      create(:student_identity, student: student)
    end
  end
end

spec/factories/profiles.rb

FactoryGirl.define do
  factory :profile do
    birthday { Faker::Date.birthday(15, 150) }
    sequence(:email) { |i| "profile_#{i}@email.com" }
    first_name { Faker::Name.first_name }
    last_name { Faker::Name.first_name }
    password { Faker::Internet.password(6, 72, true, true) }
  end
end

spec/factories/student_identities.rb

FactoryGirl.define do
  factory :student_identity do
    provider { ['facebook.com', 'google.com', 'twitter.com'].sample }
    uid { Faker::Number.number(10) }
  end
end

spec/requests/authorizations_spec.rb

require 'rails_helper'

RSpec.describe 'Authorizations', type: :request do
  describe 'POST /v1/authorizations/sign_in' do
    let!(:student) { create(:student_with_profile_and_identity) }

    context 'when the request is valid' do
      subject do
        post '/v1/authorizations/sign_in',
             params: credentials
      end

      context "user signs up via social network" do
        let(:credentials) do
          {
            authorization: {
              student: {
                profile_attributes: {
                  email: student.profile.email
                },
                student_identities_attributes: {
                  provider: student.student_identities[0].provider,
                  uid: student.student_identities[0].uid
                }
              }
            }
          }
        end

        it 'returns an authentication token' do
          subject
          p "1 student.profile.inspect #{student.profile.inspect}"
          expect(json['token']).to(be_present)
        end
      end

      context 'when the user has already an account' do
        let(:credentials) do
          {
            authorization: {
              email: student.profile.email,
              password: student.profile.password
            }
          }
        end

        it 'returns an authentication token' do
          p "2 student.profile.inspect #{student.profile.inspect}"
          subject
          expect(json['token']).to(be_present)
        end
      end
    end
  end
end

几乎所有测试都通过了...问题是:

它在每种情况下都在创建一个新的学生。我希望 let!(:student) { ... } 类似于 "singleton",换句话说,一旦在此处创建/定义 let!(:student) { create(:student_with_profile_and_identity) },它将不再被调用。

例如:日志是这样的:

"1 student.profile.inspect #<Profile id: 1, email: \"profile_1@email.com\", profileable_type: \"Student\", profileable_id: 1>"

"2 student.profile.inspect #<Profile id: 2, email: \"profile_2@email.com\", profileable_type: \"Student\", profileable_id: 2>"

虽然我希望实例是相同的。

我错过了什么吗?

【问题讨论】:

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


    【解决方案1】:

    在 RSpec 中,let and let! 是一样的,只是 let 是惰性的,let! 是急切的:

    使用let 定义一个记忆化的辅助方法。该值将在同一示例中的多个调用中缓存,但不会跨示例缓存。

    注意let 是惰性求值的:直到它定义的方法第一次被调用时才会求值。您可以使用let! 在每个示例之前强制调用该方法。

    如果您希望在所有示例中都保留某些内容,则可以使用 before hook...before(:context) 听起来可能就是您想要的。您也许可以设置一个在 before 块中记忆的辅助方法,以避免在任何地方都必须使用实例变量(根据 this comment):

    def student
      @student ||= create(:student_with_profile_and_identity)
    end
    
    before(:context) do
      student # force student creation
    end
    

    【讨论】:

      猜你喜欢
      • 2018-12-31
      • 2011-04-28
      • 2019-06-28
      • 2013-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-17
      • 1970-01-01
      相关资源
      最近更新 更多