jake
jake

Reputation: 1665

Rails Capybara Test has empty Instance Variable

I have a controller sending in a list of vendors to my controller, and on normal view it's working fine.

class VendorsController < ApplicationController

  respond_to :html, :json

  def index
    @vendor_categories = VendorCategory.where(:is_top_level => true)
    @vendors = Vendor.includes(:vendor_tier, :vendor_categorizations, :vendor_categories).order('vendor_tier_id DESC, name ASC')

    respond_with @vendors
  end
end

In my view I have the following two lines:

= debug @vendors
= debug current_user.user_vendor_choices

which, again, are working if I view it in the browser. However, if I test it with Capybara and RSpec, it's empty.

require 'spec_helper'

describe 'Vendors' do
  before do
    category = create(:vendor_category)

    5.times do 
      vendor = create(:vendor)
      vendor_categorization = create(:vendor_categorization, vendor: vendor, vendor_category: category)
      p vendor
      p category
      p vendor_categorization
    end

    visit signup_path

    @new_user = sign_up
  end

  before(:each) do
    visit destroy_user_session_path
    visit new_user_session_path
    sign_in @new_user
    visit vendors_path
  end

  it 'should save selected vendors', js: true do
    p Vendor.includes(:vendor_tier, :vendor_categorizations, :vendor_categories).order('vendor_tier_id DESC, name ASC').count
  end

end

Vendor.all and the above Vendor.includes... both return values, but for some reason in my test it's not showing anything... getting a Capybara::Element not found.

UPDATE

For testing purposes, I created the Vendors directly with the controller:

  def index

    @vendor_categories = VendorCategory.where(:is_top_level => true)
    4.times do
      Vendor.create({name: 'Test McTesterson', vendor_tier_id: 1})
    end
    @vendors = Vendor.includes(:vendor_tier, :vendor_categorizations, :vendor_categories).order('vendor_tier_id DESC, name ASC')

    respond_with @vendors
  end

Spec passes. What the--? This must be a FactoryGirl issue, or for some reason my records are deleted before it can run the test? Consoling the objects after I create them is showing a record with an ID, which I guess doesn't prove that it's putting them in the database...

Upvotes: 1

Views: 1240

Answers (2)

james
james

Reputation: 4049

Hi I cursorily glanced at this question, not sure you even need the help anymore, but I think the reason this is failing is a fundamental set up issue that your answer is just patching around.

When you're running a js: true spec (by the way, js: true should be on the describe line, not the it line), short version, Capybara works in different threads, so instance variables created in a before block, unlike with regular Rspec testing, are not available in the spec. To make them available, you have to use a truncation cleaning strategy.

RSpec.configure do |config|
  config.use_transactional_fixtures = false

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

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

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

TL;DR when running a js test, truncation is basically required (unless obviously you're running js tests that don't require any database interactions). When running all other tests, use transactions (because it's also much faster). I guess your answer replicated this to some extent =)

Upvotes: 0

jake
jake

Reputation: 1665

Turns out my Database Cleaner activities defined in my spec_helper were a little too vigorous. I had:

RSpec.configure do |config|

  config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end

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

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

end

I had to get rid of the second chunk, so it now reads:

RSpec.configure do |config|

  config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end

end

And it works! Not really sure why... any ideas (aside from the obvious, before it was calling database cleaner before/after each test)?

Upvotes: 1

Related Questions