Given the following simple spec:
require 'spec_helper'
feature 'Feeds', %q{
In order see the latest content of Buurtlink
As a user
I should be able to view a neighborhood or postal_code
} do
background do
#neighborhood = FactoryGirl.create(:neighborhood_with_posts)
end
scenario 'Visitor views a neighborhood' do
visit neighborhood_path(#neighborhood)
find('#header_title').should have_content 'Diemen-Zuid'
10.times do |index|
expect(page).to have_text "Title of new post #{index}"
end
end
end
This test randomly fails. No JS is used on the page, but Capybara seems to visit the neighborhood_path before FactoryGirl is done creating all necessary posts. When looking at the page using save_and_open_page I can see that sometimes not all posts have been created yet.
Simply adding sleep 1 above visit neighborhood_path fixes the problem, but that's not a solution.
I'm using RSpec, Capybara, Spork and DatabaseCleaner. I also monkey-patched ActiveRecord so that it uses a shared connection.
Try this instead of your background block:
let!(:neighborhood){FactoryGirl.create(:neighborhood_with_posts)}
and you can do
visit neighborhood_path(neighborhood)
This was in the ApplicationController:
def load_posts_after_current_time
session[:load_posts_after] = DateTime.now unless params[:page].present?
end
Therefore, errors occurred when fetching newly created records. So added created_at 1.hour.ago to the post factory.
factory :post do
user { FactoryGirl.create(:user) }
created_at 1.hour.ago
factory :neighborhood_post do
association :postable, factory: :_neighborhood_post
end
end
It works!
Related
I'm a newcomer when it comes to both Capybara FactoryGirl, In my Rails app I have a relationship that looks like this:
# App.rb
belong_to :plan
# Plan.rb
has_many :apps
Each app must have a plan, in my App.rb model I do it like this: before_save :set_default_plan, on: :create.
I want to test that app creation works, using Capybara integration tests. I currently have a test that looks like this:
require "rails_helper"
include Warden::Test::Helpers
Warden.test_mode!
describe "adding apps" do
let(:user) { FactoryGirl.create(:user) }
before { login_as(user, scope: :user) }
it "allows a user to create an app" do
visit apps_path
fill_in "App name", with: "My App"
click_on "create_app_button"
visit apps_path
expect(page).to have_content("My App")
end
end
After I create apps I render this in my view: #{app.plan.free_requests}. If I run my tests with bundle exec rspec I currently get this error:
undefined method `free_requests' for nil:NilClass
In my app I also use FactoryGirl to test my models. I have the following (relevant) factories:
FactoryGirl.define do
factory :app do
name "Test"
[...]
association :plan, :factory => :plan
end
end
FactoryGirl.define do
factory :plan do
name "Default"
[...]
end
end
I wonder how I should set ut my factories and test suite to make this test become a green one.
Can I either assign a plan to the app I'm creating somehow with Capybara, or can I create a default association / plan for my app with FactoryGirl. Is is there another approach? Thankful for all assistance.
Update
This is how my set_default_plan method looks:
# App.rb
def set_default_plan
if self.new_record?
plan = Plan.find_by_stripe_id("default_plan")
if plan.nil? == false
self.plan = plan
end
end
end
FactoryGirl really shouldn't have anything to do with "apps" or "plans" in your test, since you're running through your controllers create action, unless set_default_plan doesn't actually create a plan if none exist. If that is the case then you could use FactoryGirl to create the required plan like - FactoryGirl.create(:plan) in your before block
You should also specify that the plan is a required association (this is default in Rails 5 so if you're using that this may not be necessary) which will prevent your Apps from being created without a plan.
# App.rb
belongs_to :plan, required: true
Another thing to note is you should always check for confirmation after clicking a button that performs an action before visiting another page. This is because the result of clicking the button is not guaranteed to be synchronous so visiting another page immediately can kill the action request.
click_on "create_app_button"
expect(page).to have_content("App Created!!!") # whatever text is shown on success
visit apps_path
I added an import method to a controller, and it works just fine when I test it manually from my website, but it is failing in rspec. Here is what my test looks like:
require 'spec_helper'
describe PropertiesController do
let!(:user) { FactoryGirl.create(:user) }
before :each do
sign_in user
end
describe "should upload user properties" do
before do
post :import, spreadsheet: fixture_file_upload("/files/property_upload_template.xlsx")
end
it "should have created records" do
expect(Property.count).to eq 3
# Some other assertions
end
end
end
When I add puts statements inside my import action, including on the very first line, none of them are apparently invoked. The test is generating no errors other than failing the assertions. Similarly, when I look at the test.log file, all that happens is the creation of my test user (and a devise confirmation email gets sent out), but it doesn't appear that the import action is ever hit. The test server seems to recognize the route fine, but it's not actually executing the action.
Is there something wrong with my test configuration?
I had been banging my head for a good couple hours, but I just figured it out. I needed to confirm the user in my user factory. I guess since I enabled the confirmable module in devise, and the user wasn't confirmed, it was silently not allowing me to authenticate...
... Would sure be nice if rspec/rails/devise generated some sort of error pointing me to the problem here.
For the sake of completeness, I'm adding in the code for confirming a user in the version of FactoryGirl at the time of that writing:
FactoryGirl.define do
factory :confirmed_user, :parent => :user do
after(:create) { |user| user.confirm! }
end
end
i'm using rspec/capybara in my rails project for tests, which is working fine with the default driver. But when i switch to webkit or selenium i get logged out after every request that i make.
This code is working as expected, i see the logged in page 2 times:
require 'rails_helper'
feature 'test' do
scenario 'this' do
user = FactoryGirl.create :user
login_as(user)
visit root_path
save_and_open_page
visit root_path
save_and_open_page
end
end
When i set webkit or selenium as driver only the first page is the logged in version, on the second page i'm logged out:
require 'rails_helper'
feature 'test' do
scenario 'this', driver: :webkit do
user = FactoryGirl.create :user
login_as(user)
visit root_path
save_and_open_page
visit root_path
save_and_open_page
end
end
How can i fix this?
I was having this exact same problem and eventually came across this question with pretty much the same problem: Why is Capybara discarding my session after one event?
The solution is to include the snippet found here in your rails_helper
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
This should be very simple to test, but for some reason my test is failing, please consider the following, model bit:
class User < ActiveRecord::Base
def active!
update_attribute(:active, true)
end
end
controller :
def activate
user = User.find_by_uuid(params[:id])
user.active!
puts "id #{user.id}"
end
test :
describe 'activate user' do
let(:user) { FactoryGirl.create(:user) }
before do
sign_in user
visit activate_user_path(id: user.uuid)
puts "id #{user.id}"
end
it 'should be true' do
save_and_open_page
user.active.should be_true
end
end
This test fails :
expected: true value
got: false
But when I do it with browser, the user gets activated without problems. What am I doing wrong? This really looks like a sily test but still doesn't pass, I've spend more than one hour trying out different stuff, none of which worked.
The problem is that the spec still holds the User in the user variable that was created via FactoryGirl and does not know that is was changed in the database. Just reload the user and it should work:
it 'should be true' do
save_and_open_page
user.reload.active.should be_true
end
Btw. if active is a boolean you can also spec it this way (what reads much nicer):
user.reload.should be_active
Trying to write some tests for code I've already written, with a view to extending my code using test-driven development.
I have a controller whose index action calls a 'user_info' method, which just collects together some instance variables relying on Sorcery's current_user variable. For example:
def user_info
#current_A = current_user.a
#current_B = current_user.b
end
def index
user_info
// rest of the method goes here
end
I started writing some tests using rspec, just to get a feel for testing this code base. My controller spec is very basic and looks like this:
describe MyController do
describe "GET 'index'" do
get 'index'
response.should be_success
end
end
However, I get the following error when I try to run this spec:
NoMethodError: undefined method 'a' for false:FalseClass
First of all, how do I get my spec to recognize the Sorcery method current_user? And, out of curiosity, why is current_user being flagged as an instance of FalseClass? If it's not calling the Sorcery method, (and I haven't defined current_user anywhere else in my code), should it not appear as nil?
To use Sorcery test helpers you need the following lines in your spec_helper.rb.
The following needs to be in the Rspec.configure block:
RSpec.configure do |config|
config.include Sorcery::TestHelpers::Rails
end
After you have this in place you can use the Sorcery test helpers. For a Controller test you would add the following to your test.
#user = either a fixture or a factory to define the user
login_user
If you don't want to specify #user you can pass an argument.
login_user(fixture or factory definition)
Once you login the current_user should be available to your tests.
logout_user is also available.
See the Sorcery Wiki for information on setting up a user fixture to work with the login_user helper.
Richard, the problem is likely that you don't have a current_user.
To do that, you need to simulate the login process.
You can do that with a controller spec, but I don't have a good example here. I was writing specs on existing code, like you, and it made sense to use request specs instead.
I also don't have one for Sorcery (I should!!) and I am here using Capybara for filling in forms,. Still, here is how my spec looked:
(Here :account is the same as :user would be)
context "when logged in" do
before :each do
#account = Factory.create(:account)
#current_game = Factory(:game_stat)
visit login_path
fill_in 'Username or Email Address', :with => #account.email
fill_in 'Password', :with => #account.password
click_button('Log in')
end
So factories are another matter, mine looked like this:
Factory.define :account do |f|
f.sequence(:username) { |n| "ecj#{n}" }
f.sequence(:email) { |n| "ecj#{n}#edjones.com" }
f.password "secret"
f.password_confirmation {|u| u.password }
end
You don't have to use factories, but you do need to get that session and current_user established.
On important bit is to ensure the user is activated after creation if you're using the :user_activation submodule of Sorcery.
So, if you're using the fabrication gem, that would look like,
Fabricator(:properly_activated_user, :from => :user) do
after_create { |user| user.activate! }
end
As #nmott mentioned you need to do two things:
1) Register text helper methods using:
RSpec.configure do |config|
config.include Sorcery::TestHelpers::Rails
end
2) In your example access current_user through controller.current_user like that:
login_user(user)
expect(controller.current_user).to be_present