Slevin
Slevin

Reputation: 4232

Models created with FactoryGirl aren't available in controller

Original question

I'm trying to write some feature tests with RSpec and Capybara and wonder, why my gon object is always empty allthough it was set in my controller:

app/controllers/timetrackings_controller.rb:

class TimetrackingsController < ApplicationController
  include ApplicationHelper

  before_action :authenticate_user!
  before_action :set_timetracking, only: [:update, :destroy]

  # GET /timetrackings
  def index
    projects = Project.all_cached.select('id, name, category, number, customer_id').includes(:customer).where(:archived => false).order(:number)
    gon.projects = group_by_customer(projects).to_h
    gon.services = Service.all_cached.select('id, name').where(:archived => false).order('LOWER(name)')
  end

  ...

Now I've wondered why this data doesn't get rendered into my view (debugged with save_and_open_page: //<![CDATA[ window.gon={};gon.projects={};gon.services=[]; //]]>), so I just tried to get the gon values in my test file:

require 'spec_helper'

describe 'the timetracking page', :js => true do

  before :each do
    switch_to_subdomain('test')

    @project = FactoryGirl.create(:project)
    @service = FactoryGirl.create(:service)

    user = FactoryGirl.create(:user)
    login_as(user, :scope => :user)

    visit '/timetracking'

    save_and_open_page
  end


  it 'renders the react component' do
    expect(page).to have_selector '#timetracking-form'
  end

  it 'allows me to create a new timetracking' do
    within('#timetracking-form') do
      fill_in 'duration', :with => '2'

      puts '########'
      puts Gon.all_variables
      puts Project.all.to_json
      puts Service.all.to_json
      puts '########'

      find_field('project_id').find("option[value='#{@project.id}']").click
    end
    click_button '.ei-icon-check'
  end

end

But the output is just:

########
{}
[{"id":2,"number":"2","name":"project2","description":"Some notes","archived":false,"customer_id":2,"created_at":"2015-11-30T16:05:40.160+01:00","updated_at":"2015-11-30T16:05:40.160+01:00","rate_type":null,"hourly_rate":null,"service_rates":null,"budget_type":null,"budget_rate":null,"category":"A","deadline":null}]
[{"id":2,"name":"service2","description":"Some notes","archived":false,"created_at":"2015-11-30T16:05:40.173+01:00","updated_at":"2015-11-30T16:05:40.173+01:00","billable":null,"hourly_rate":null}]
########

Thats my spec_helper.rb:

require 'devise'

# Setup Capybara
require 'capybara/rspec'
require 'capybara/poltergeist'
Capybara.always_include_port = true
Capybara.javascript_driver = :poltergeist

# Use SimpleCov for code coverage
require 'simplecov'
require 'simplecov-shield'
SimpleCov.start 'rails'
SimpleCov.formatters = [
  SimpleCov::Formatter::HTMLFormatter,
  SimpleCov::Formatter::ShieldFormatter
]

# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'
require 'shoulda-matchers'

# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)

module ControllerMacros
  def attributes_with_foreign_keys(*args)
    FactoryGirl.build(*args).attributes.delete_if do |k, v|
      ['id', 'type', 'foreign_id', 'foreign_type', 'created_at', 'updated_at'].member?(k)
    end
  end
end

RSpec.configure do |config|

  config.use_transactional_fixtures = false
  config.use_instantiated_fixtures  = false
  config.mock_with :rspec

  # Use FactoryGirl for fixtures
  config.include FactoryGirl::Syntax::Methods

  # Auto-detect spec types
  config.infer_spec_type_from_file_location!

  # Insert devise helpers in controller specs
  config.include Devise::TestHelpers, type: :controller
  config.include Warden::Test::Helpers
  config.extend ControllerMacros, type: :controller
  config.include ControllerMacros


  # Run specs in random order to surface order dependencies. If you find an
  # order dependency and want to debug it, you can fix the order by providing
  # the seed, which is printed after each run.
  #     --seed 1234
  config.order = 'random'

  config.before(:suite) do

    Warden.test_mode!

    # Clean all tables to start
    DatabaseCleaner.clean_with :truncation

    # Use transactions for tests
    DatabaseCleaner.strategy = :transaction

    # Truncating doesn't drop schemas, ensure we're clean here, app *may not* exist
    Apartment::Tenant.drop('test') rescue nil

    # Create the default tenant for our tests
    Account.create!(name: 'Test', domain: 'test', email: '[email protected]')

  end

  config.before(:each) do

    # Start transaction for this test
    DatabaseCleaner.start

    # Switch into the default tenant
    Apartment::Tenant.switch! 'test'

    # Use Timecop to freeze times on time-critical tests
    Timecop.return

  end

  config.after(:each) do

    # Reset tentant back to `public`
    Apartment::Tenant.reset

    # Rollback transaction
    DatabaseCleaner.clean

  end

end

Update

OK, I think the problem lies elsewhere:

After creating models with FactoryGirl (like FactoryGirl.create(:project)), the records aren't available in the controller. If I write something like

@foo = Project.all.to_json

in my controller and want to display this data in my view, I just get [] (after calling save_and_open_page).

I though FactoryGirl.create writes data directly to the DB? Why isn't the data available in my controller methods?

Update 2

Adding

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

solves this problem, but now I get:

 Failure/Error: user = FactoryGirl.create(:user)
     ActiveRecord::RecordNotUnique:
       PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_users_on_lastname"
       DETAIL:  Key (lastname)=(Kautzer) already exists.
       : INSERT INTO "users" ("email", "initial", "firstname", "lastname", "encrypted_password", "archived", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"

I thought the database gets cleaned after each test?

Update 3

Disregard, the last problem was a mistake in my database setup.

Upvotes: 2

Views: 547

Answers (1)

Slevin
Slevin

Reputation: 4232

Adding

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

to my spec_helper.rb solves the problem :)

Upvotes: 1

Related Questions