ControllerMacros method not being loaded by RSpec - ruby-on-rails

I've added a ControllerMacro but the method isn't being made available in my specs. My specs will fail with:
NoMethodError: undefined method `attributes_with_foreign_keys' for FactoryGirl:Module
I'm trying to do this following this discussion on Github. I've looked at other similar questions but most point at using config.include ControllerMacros, :type => :controller instead of config.extend ControllerMacros, :type => :controller which I'm doing already.
Now I know that the controller_macros.rb file is being loaded upon starting the test as I've run the specs through RubyMine's debugger but why the method isn't available is beyond me!
spec_helper.rb
require 'simplecov'
SimpleCov.start 'rails'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'email_spec'
require 'rspec/autorun'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include(EmailSpec::Helpers)
config.include(EmailSpec::Matchers)
config.include ControllerMacros, :type => :controller
config.include FactoryGirl::Syntax::Methods
config.use_transactional_fixtures = true
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
spec/support/macros/controller_macros.rb
module ControllerMacros
def attributes_with_foreign_keys(*args)
FactoryGirl.build(*args).attributes.delete_if do |k, v|
["id", "type", "created_at", "updated_at"].member?(k)
end
end
end
This example controller spec:
describe "POST create" do
describe "with valid params" do
it "creates a new Course" do
expect {
#post :create, course: FactoryGirl.build(:course)
post :create, course: FactoryGirl.attributes_with_foreign_keys(:course)
}.to change(Course, :count).by(1)
end

The reason of failing:
The macro you defined is a method within a module. You asked Rspec to include the module, so the method is available to Rspec's instance, which should be used similar to describe, it etc.
However, you used this method as a FactoryGirl's class method. Obviously this is not working.
The solution.
It looks you want to have a convenient method to quickly build a set of pre-defined attributes of a model in a special case.
This logic is better not to be a "macro", because it's better to use FactoryGirl as a centralized place to store all logic about models.
So, the most convenient way is, beyond your normal factories, define a special factory for this case, say foo, with all association and other logic you need inside it, and inherit from other normal factory.
If it's name is foo, then you can easily build a hash of foo's attributes like FactoryGirl.attributes_for :foo

Instead of calling:
post :create, course: FactoryGirl.attributes_with_foreign_keys(:course)
Just call:
post :create, course: attributes_with_foreign_keys(:course)
Took advice from #Billy Chan answer.

Related

RSpec: FactoryBot is seeing duplicate definitions

I'm still new to using FactoryBot so I might be missing something. I am getting this error message:
Could it be due to improper set up in the spec_helper.rb file?
As for defining the user.rb factory, I tried including "associations: contracts" in the user.rb file. I'm still not sure if I should be doing that or is this current format fine for Rspec to pick up the association with contracts.rb?
Any help is appreciated! Thanks!
spec_helper.rb
require 'factory_bot_rails'
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
FactoryBot.definition_file_paths = [File.expand_path('../factories', __FILE__)]
FactoryBot.find_definitions
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
spec/factories/users.rb
FactoryBot.define do
factory :user do
full_name "Test tester"
email "test#tester.com"
password "123456"
end
end
spec/factories/contracts.rb
FactoryBot.define do
factory :contract do
vendor "O2"
starts_on "2019-03-08"
ends_on "2019-03-10"
price 30
end
end
spec/requests/contracts_api_spec.rb
require 'rails_helper'
RSpec.describe "ContractsApi", type: :request do
describe "POST #create" do
before(:each) do
#user = FactoryBot.create(:user)
#current_user = AuthenticateUserCommand.call(#user.email, #user.password)
#contract = #current_user.contracts.create(vendor: "Lebara", starts_on: "2018-12-12", ends_on: "2018-12-14", price: "15")
end
it 'creates a new contract' do
expect { post api_v1_contracts_path, params: #contract }.to change(Contract, :count).by(1)
end
end
end
I believe you don't need to configure FactoryBot in your spec_helper.rb and what you are doing there maybe be causing FactoryBot to load the factories twice.
Try changing the content of spec_helper.rb to just:
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
Also, considering that you are including FactoryBot::Syntax::Methods, in your tests you can simply use #user = create(:user) instead of #user = FactoryBot.create(:user)

Undefined method "contain" for controller spec

It's definitely best to divide specs up so you have specs pertaining to each aspect of the MVC architecture, but I think there is a slight crossover with controller specs and view specs.
With view specs, you should only be concerned with the view, but with controller specs I still think it would be a good idea to test that the correct view is rendered, and maybe even test the content of the view, although more in-depth testing of the content should take place in the view spec.
Despite this clear article, https://www.relishapp.com/rspec/rspec-rails/v/2-1/docs/controller-specs/render-views, describing how to do this, I just cannot integrate my view and controller specs.
I keep getting the error undefined method 'contain'!
Here's my spec_helper:
# 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 'capybara/rspec'
require 'capybara/rails'
require 'factory_girl_rails'
require 'ap'
def set(factory)
#user = FactoryGirl.create(factory)
end
def sign_up(first_name, last_name, profile_name, email, password)
visit "/"
click_link "Register"
fill_in('First name', with: first_name)
fill_in('Last name', with: last_name)
fill_in('Profile name', with: profile_name)
fill_in('Email', with: email)
fill_in('Password', with: password)
fill_in('Password confirmation', with: password)
click_button 'Sign up'
end
def sign_in(email, password)
visit "/"
click_link "Sign In"
fill_in('Email', with: email)
fill_in('Password', with: password)
click_button 'Sign in'
end
def sign_out
visit "/"
click_link "Sign Out"
end
#Webrat.configure do |config|
# config.mode = :rails
#end
#webrat
require 'capybara/poltergeist'
# Capybara.javascript_driver = :poltergeist
Capybara.javascript_driver = :selenium
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)
RSpec.configure do |config|
# true means 'yes, filter these specs'
config.filter_run_excluding stress: true
# config.current_driver = :webkit
# config.use_transactional_fixtures = false
# config.include Capybara::DSL
DatabaseCleaner.strategy = :truncation
config.after(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
# config.before(:suite) do
# DatabaseCleaner.strategy = :transaction
# DatabaseCleaner.clean_with(:truncation)
# DatabaseCleaner.start
# end
# config.after(:each) do
# DatabaseCleaner.clean
# end
#config.after(:suite) do
# DatabaseCleaner.strategy = :transaction
# DatabaseCleaner.clean_with(:truncation)
# DatabaseCleaner.clean
# end
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
# config.fixture_path = "#{::Rails.root}/spec/fixtures"
# config.include RSpec::Rails::RequestExampleGroup, type: :feature
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
I18n.enforce_available_locales = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# 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"
end
Here's my controller spec:
require "spec_helper"
describe UserFriendshipsController, type: :controller do
render_views
let (:user_1) { FactoryGirl.create(:user_1)}
before {
sign_in user_1
get :index
}
it "renders the :index view" do
response.should render_template(:index)
end
it "view contains expected html" do
# a sanity test more than anything
response.should contain("Welcome to the home page")
end
end
Upon running this spec I get this:
.F
Failures:
1) UserFriendshipsController view contains expected html
Failure/Error: response.should contain("Listing widgets")
NoMethodError:
undefined method `contain' for #<RSpec::Core::ExampleGroup::Nested_1:0x00000008632268>
# ./spec/controllers/user_friendships_spec.rb:18:in `block (2 levels) in <top (required)>'
Finished in 0.1835 seconds
2 examples, 1 failure
Why is this happening? How can I get this to work?
If you look at the relish documentation for the current 2.14 version of Rspec you'll see that they're using match now instead:
expect(response.body).to match /Listing widgets/m
Using the should syntax, this should work:
response.body.should match(/Welcome to the home page/)
Right, it was a very unclear article from Rspec that caused this error. It was using webrat in its example and didn't think to tell you. If anyone else gets here, you can add webrat to your gemfile to use the contain method:
Gemfile
group :test do
gem 'webrat'
end
However, it makes a lot more sense to use rspec's native match method:
expect(response.body).to match /Listing widgets/m

Rspec cannot find custom module

I try to test devise user authentication, the problem I've done everything according to samples, however the code still doesn't work.
spec/support/devise/devise_support.rb
module ValidUserRequestHelper
def sign_in_as_a_valid_user
#user ||= Fabricate(:simple_user)
post_via_redirect user_session_path, 'user[email]' => #user.email, 'user[password]' => #user.password
end
end
RSpec.configure do |config|
config.include ValidUserRequestHelper, :type => :request
end
spec/spec_helper.rb
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
However when I run test, it fails on the calling to `sign_in_as_a_valid_user'
undefined local variable or method `sign_in_as_a_valid_user' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0xc57a0c4>
I don't have idea how to debug this.
The test code is
require 'spec_helper'
describe User do
before do
sign_in_as_a_valid_user
end
...
when you write this in your rspec configuration
RSpec.configure do |config|
config.include ValidUserRequestHelper, :type => :request
end
you tell rspec to only include this helper for request spec. those are typically located in spec/request. deriving from the example of your spec that has describe User in it, i assume that you are writing a model spec, typically located in spec/model. so when running the spec, rspec won't include it for that spec!
if you just remove the :type => :request it will get included everywhere. keep in mind, that there is usually a good reason for this kind of restrictions. for example a helper that only works with a fake browser, like it is done in request specs.

Capybara methods are undefined

I cannot get capybara to work. I am using capybara 2.0.0
I get this error
Failure/Error: visit "/users/sign_in"
NoMethodError:
undefined method `visit' for #<RSpec::Core::ExampleGroup::Nested_21:0x007fdda4c6eba0>
on this spec
spec/requests/forgot_password_spec.rb
describe "forgot password" do
it "redirects user to users firms subdomain" do
visit "/users/sign_in"
end
end
I do not get any errors that it cannot find capybara and it's included in the spec_helper.rb
spec_helper.rb
require 'rubygems'
require 'spork'
require 'database_cleaner'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rspec'
require 'rspec/autorun'
require 'factory_girl'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
config.include RequestMacros, :type => :request
config.mock_with :rspec
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.infer_base_class_for_anonymous_controllers = false
end
Spork.each_run do
FactoryGirl.reload
end
end
Has anybody else encountered this?
If you've got version >= 2.0, any tests that use Capybara methods like visit should go under a spec/features directory, and not under spec/requests, where they'd normally reside in Capybara 1.1.2.
Have a look at the following links for more information:
rspec-rails and capybara 2.0: what you need to know
rspec-rails gem Capybara page
If you don't want to use a spec/features directory, you should be able to mark a test as a feature in the following way and have Capybara methods work:
describe "Some action", type: :feature do
before do
visit "/users/sign_in"
# ...
end
# ...
end
In my case, I got this error because I forgot to putrequire "spec_helper"
at the top of my new spec file.
I've done it enough times that I'm adding an answer to an already answered question in hopes that it helps some other knucklehead (or most likely me searching this again in the future).

Is this call to sleep in my RSpec request spec valid?

I'm clicking show and destroy links with Capybara that should be in the first table row in an employees table. The table should sort so that the most recently modified employee is on the top, based on the updated_at timestamp.
Before the example a valid employee must be created that passes authentication. Then at the beginning of the example an employee must be created to test with. This is the employee that should always be at the top of the table because it's supposed to have been modified most recently. Sometimes it is and sometimes it isn't. Adding a sleep call fixes this, and I'm wondering if that's valid or if I've got it wrong. I thought adding a sleep call in a test was a Bad Thing. I also thought if the authentication employee is created before the example then even if my spec is running really fast then it should still have an earlier updated_at timestamp.
The authentication macro:
module AuthenticationMacros
def login_confirmed_employee
# create a valid employee for access
Factory :devise_confirmed_employee,
:username => 'confirmed.employee',
:password => 'password',
:password_confirmation =>'password'
# sign in with valid credentials
visit '/employees/sign_in'
fill_in 'Username', :with => 'confirmed.employee'
fill_in 'Password', :with => 'password'
click_on 'Sign In'
end
end
The request spec in question:
require 'spec_helper'
include AuthenticationMacros
describe "Employees" do
before do
login_confirmed_employee
end
# shows employees
it "shows employees" do
sleep 1.seconds
Factory :employee_with_all_attributes,
:username => 'valid.employee',
:email => 'valid.employee#example.com',
visit '/employees'
within 'tbody tr:first-child td:last-child' do; click_on 'Show', end
page.should have_content 'valid.employee'
page.should have_content 'valid.employee#example.com'
end
# destroys employees
it "destroys employees" do
sleep 1.seconds
Factory(:employee)
visit '/employees'
within 'tbody tr:first-child td:last-child' do; click_on 'Delete', end
page.should have_content 'Employee was successfully deleted.'
end
end
And for good measure here's my spec_helper:
require 'spork'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
ActiveSupport::Dependencies.clear
require 'email_spec'
RSpec.configure do |config|
config.include EmailSpec::Helpers
config.include EmailSpec::Matchers
end
end
Spork.each_run do
require 'rspec/rails'
require 'factory_girl_rails'
require 'capybara/rspec'
require 'capybara/rails'
require 'shoulda'
RSpec.configure do |config|
config.mock_with :rspec
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
end
Sometimes I end up on the show page or destroying the authentication-required employee instead of the example employee.It never happens though if I add the 1 second sleep call.
Nope, it fact it's a very bad solution because it increases tests execution time.
You could mock Time.now method call using one of the following solutions:
https://github.com/bebanjo/delorean
https://github.com/jtrupiano/timecop

Resources