james
james

Reputation: 4049

Rspec variable created in before block is deleted by the time feature spec reaches POSTed controller action

I have a truncation database cleaning strategy, so not sure why else this is happening. Basically just doing a single feature spec to test that an order gets created appropriately.

require 'rails_helper'

describe "create successfully", type: :feature, js: true do
  before do
    @site = create(:site)
    visit "/orders"
    .... # various actions to build an order using the page's form
    puts ">>>>>"
    puts "site in before action: #{Site.all.size}"
    find("#checkoutModal #submit").click()
    sleep(1)
  end
  it "should create" do
    expect(Order.all.size).to equal(1)
  end
end

# controller action that #submit POSTs to

def create
  puts ">>>>>"
  puts "site in controller create: #{Site.all.size}"
  @order = Order.new(order_params)
  @order.save if @order.valid?
end

# puts output:
>>>>>
site in before action: 1
>>>>>
site in controller create: 0

The spec fails because @order creation depends on a @site. Any thoughts on why the @site is being destroyed? Again I do have a truncation set up correctly:

# rails_helper.rb

Rspec.configure do |config|
  config.use_transactional_fixtures = false
  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, js: true) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each, truncate: true) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end
end



Upvotes: 1

Views: 193

Answers (1)

max
max

Reputation: 101811

A better way to test this altogether is to use the change matcher:

Rspec.feature "Creating orders", js: true do

  let!(:site ) { create(:site) }

  def fill_in_and_submit_form
    visit "/orders"
    # ...
    fill_in "something", with: attributes[:something]
    find("#checkoutModal #submit").click()
  end

  context "with valid attributes" do
    let(:attributes){ attributes_for(:order) }
    it "creates an order" do
      expect do
        fill_in_and_submit_form
      end.to change(Order, :count).by(1)
    end
  end

  context "with invalid attributes" do
    let(:attributes) do
       {} # should be a hash with invalid attributes
    end
    it "does not create an order" do
      expect do
        fill_in_and_submit_form
      end.to_not change(Order, :count)
    end
  end
end

This creates a count query before and after the block is evaluated. One thing you should bear in mind with .size is that will return the length of the collection if it already has been loaded. Which is not a good thing here as you need a db count.

Naming your top level feature description "create successfully" is not a good idea. It does not describe what you are testing it would require you to creates two files to test for success and failure.

That controller is also just plain wrong.

def create
  @order = Order.new(order_params)
  if @order.save
    redirect_to @order
  else
    render :new
  end
end

@order.save if @order.valid? is silly. .save will validate the record and persist it if its valid. You really just want to check the return value of save to see if the record has actually been saved to the database. If orders is a nested record it should actually look like this as well:

def create
  @site = Site.find(params[:site_id]) # you're not passing it through a hidden input are you?
  @order = @site.orders.new(order_params)
  if @order.save
    redirect_to @order
  else
    render :new
  end
end

Upvotes: 0

Related Questions