【问题标题】:Solidus: Code that is executed in after_save callback returns error in rspecSolidus:在 after_save 回调中执行的代码在 rspec 中返回错误
【发布时间】:2017-08-05 21:11:43
【问题描述】:

我正在使用最新版本的 Solidus 电子商务(Spree 的分支),我遇到了这个问题。快速描述它:

  1. 在管理员中创建 Spree::Membership 记录
  2. Spree::Membership 中的 after_save 回调创建产品和 2 个变体

此代码在我运行服务器时有效,但当我尝试使用 Rspec 时,它给了我这个错误:

Failure/Error: reload.product.setup_membership_variants

 NoMethodError:
   undefined method `setup_membership_variants' for nil:NilClass
 # ./app/models/spree/membership.rb:26:in `block in setup_product'
 # ./app/models/spree/membership.rb:18:in `setup_product'
 # /box/gems/factory_girl-4.8.0/lib/factory_girl/configuration.rb:18:in `block in initialize'
 # /box/gems/factory_girl-4.8.0/lib/factory_girl/evaluation.rb:15:in `create'
 # /box/gems/factory_girl-4.8.0/lib/factory_girl/strategy/create.rb:12:in `block in result'
 # /box/gems/factory_girl-4.8.0/lib/factory_girl/strategy/create.rb:9:in `tap'
 # /box/gems/factory_girl-4.8.0/lib/factory_girl/strategy/create.rb:9:in `result'
 # /box/gems/factory_girl-4.8.0/lib/factory_girl/factory.rb:42:in `run'
 # /box/gems/factory_girl-4.8.0/lib/factory_girl/factory_runner.rb:29:in `block in run'
 # /box/gems/factory_girl-4.8.0/lib/factory_girl/factory_runner.rb:28:in `run'
 # /box/gems/factory_girl-4.8.0/lib/factory_girl/strategy_syntax_method_registrar.rb:20:in `block in define_singular_strategy_method'
 # ./spec/controllers/spree/premium_controller_spec.rb:4:in `block (2 levels) in <top (required)>'
 # ./spec/controllers/spree/premium_controller_spec.rb:33:in `block (6 levels) in <top (required)>'
 # ./spec/controllers/spree/premium_controller_spec.rb:53:in `block (8 levels) in <top (required)>'
 # ./spec/controllers/spree/premium_controller_spec.rb:53:in `block (7 levels) in <top (required)>'

这是我正在使用的代码:

membership.rb

class Spree::Membership < Spree::Base

  has_many :active_memberships, class_name: 'Spree::ActiveMembership', dependent: :destroy

  has_one :membership_product, class_name: "Spree::MembershipProduct", dependent: :destroy
  has_one :product, through: :membership_product, source: :product

  validates :name, presence: true
  validates :monthly_quota, presence: true
  validates :price, presence: true

  after_save :setup_product

  private

  def setup_product
    if product.nil?
      ActiveRecord::Base.transaction do
        create_membership_product(
          product: Spree::Product.create(
            name: name, 
            price: price, 
            shipping_category: Spree::ShippingCategory.find_by(name: "Default")
          )
        )
        reload.product.setup_membership_variants
      end
    else
      product.update(name: name, price: price)
    end
  end

end

product_decorator.rb

Spree::Product.class_eval do
  def setup_membership_variants
    ot = Spree::OptionType.find_or_create_by(name: "Membership")
    option_types << ot

    if ot.option_values.empty?
      ["Monthly", "Yearly"].each do |freq|
        ot.option_values.create(name: freq, presentation: freq)
      end
    end


    month = variants.create(is_master: false, price: price, track_inventory: false)
    month.option_values << Spree::OptionValue.find_by(name: "Monthly")
    year = variants.create(is_master: false, price: price * 12, track_inventory: false)
year.option_values << Spree::OptionValue.find_by(name: "Yearly")
  end
end

membership.rb 工厂

FactoryGirl.define do
  factory :membership, class: Spree::Membership do
    name   {FFaker::Lorem.word}
    monthly_quota { rand(10..200) }
    price { rand(10..200) }

    trait :unnamed do
      name   nil
    end

    trait :without_quota do
      monthly_quota nil
    end

    trait :priceless do
      price nil
    end
  end
end

控制器测试示例

require 'rails_helper'

RSpec.describe Spree::PremiumController, type: :controller do
  let(:valid_membership) { create(:membership) }
  let(:variant) { create(:master_variant) }

  context "#show" do
    it "returns http success" do
      get :show
      expect(response).to have_http_status(:success)
    end
    it "returns all memberships" do 
      get :show
      expect(assigns[:memberships]).to include(valid_membership)
    end
  end

  context "#create" do 
    let!(:store) { create(:store) }

    context "user not logged" do 

      context "valid attributes" do
        context "user doesn't exist in database" do
          subject do
            post(:create, params: { 
              premium: { 
                first_name: "Ondrej", 
                last_name: "Kubala", 
                email: "ondrej@bala.com", 
                password: "test123",
                password_confirmation: "test123",
                membership_id: valid_membership.id, 
                payment_frequency: "1", #monthly yearly is 2 
                cc_number: "4111111111111111", 
                cc_exp_date: "10/22", 
                cvv: "123"
              }
            }) 
          end

          it "creates user with valid attributes" do 
            expect { subject }.to change { Spree::User.count }.from(0).to(1)
          end

          it "logs in user" do 
            expect(controller.warden).to receive(:set_user)
            subject
          end

          context "create order with variant" do
            it "should handle population" do 
              expect { subject }.to change { Spree::Order.count }.by(1)
              user = Spree::User.find_by email: "ondrej@bala.com"

              order = user.orders.last
              expect(response).to redirect_to go_premium_path

              expect(order.line_items.size).to eq(1)
              # line_item = order.line_items.first
              # expect(line_item.variant_id).to eq(valid_membership.reload.product.variants.)
            end
            it "charges credit card"
            it "should redirect to account page"
          end
        end

        context "user already exists"
      end

      context "wrong user attributes" do

      end

      context "wrong payment attributes" do 

      end
    end

    context "user is logged in" do 
      let(:user) { create(:user) }

      before do
        allow(controller).to receive_messages try_spree_current_user: user
        allow(controller).to receive_messages spree_current_user: user
      end

      it "doesn't create user"
    end
  end
end

我在 Rspec 方面没有太多经验,但奇怪的是,当服务器在开发中运行但在测试中它给了我这个错误时它可以工作。

知道有什么问题吗?

【问题讨论】:

    标签: ruby-on-rails rspec solidus


    【解决方案1】:

    我的猜测是产品创建调用由于某种原因而失败。 尝试 bang version(product: Spree::Product.create!) 以在发生错误时抛出错误。

    如果确实创建了产品,请使用prybyebug 找出为什么在reload 成员资格之后,产品与membership 没有关联。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多