1

I'm using latest version of Solidus e-commerce (fork of Spree) and I'm having this issue. To describe it quickly:

  1. In Admin I create Spree::Membership record
  2. after_save callback in Spree::Membership creates Product and 2 variants

This code works when I run the server, but when I'm trying to use Rspec it gives me this error:

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)>'

Here is code I'm using:

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 factory

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

example of controller test

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

I don't have too much experience in Rspec, but it's weird that it works when server is running in development but in test it gives me that error.

Any idea what is wrong?

OndrejK
  • 187
  • 1
  • 9

1 Answers1

0

My guess would be that product creation call fails for some reason. Try bang version(product: Spree::Product.create!) to have the error thrown when it occurs.

If product indeed gets created, use pry or byebug to figure out why after reload membership, product is not associated with membership.

Vijay Agrawal
  • 1,643
  • 12
  • 17