I'm developing a sign in/out function on a web app that i'm building for learning.
During the week, i wasn't able to pass the following rspec test:
describe "sucessfull log in" do
let(:user) {FactoryGirl.create(:user)}
before do
visit signin_path
fill_in "Email", with: user.email.upcase
fill_in "Password", with: user.password
click_button 'Sign in'
end
it {should have_title(user.name)}
it {should have_link('Account')}
it {should have_link('Profile', href: user_path(user))}
it {should have_link('Sign out', href: signout_path)}
end
Then yesterday and end up re writing this code again, but forgot to add that .upcase function. I Left just .upcase.email. Suddenly, the test passed.
Ok, i'm a newbie developer... so what happaned? Why the upcase function did not let rspec log in successfully?
thanks in advance!
Why are you upcasing the email? Would you be equally surprised if .reverse made it fail? You're changing the email address.
EXAMPLE#EMAIL.COM is not the same as example#email.com, strictly speaking. It might be equivalent for your application, but you need to make the application know that.
Are you using devise? If so, the config/devise.rb file will have:
# Configure which authentication keys should be case-insensitive.
# These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email.
config.case_insensitive_keys = [ :email ]
Which should make what you're doing work. If you're not using it, whatever framework you're using will probably have a similar option, and if you're doing it yourself, you'll need to handle it yourself.
Related
I am experiencing strange very test behavior, with logged in state being handled inconsistently.
The spec logs a user in, visits a (nested or un-nested) index page, and checks that the correct content is displayed. Records are fetched asynchronously, though I don't think this should have an impact.
When each spec is run individually, they each pass. When all specs are run together, they fail because the expected content is missing. Using save_and_open_page reveals this is because the login page is being rendered, rather than the expected index page.
Why does rspec think the user is not signed in when all specs are run together, yet each spec passes individually?
The tests look something like this
let(:user) {create :user}
let(:team) {create :team}
let(:country) {create :country}
before :each do
login_as( user, scope: :user )
end
describe 'unnested' do
it 'should have the expected content', :js do
visit users_path
is_expected.to have_content "some content on the page"
end
end
describe 'nested by team' do
it 'should have the expected content', :js do
visit team_users_path(team)
is_expected.to have_content "some content on the page"
end
end
describe 'nested by nationality' do
it 'should have the expected content', :js do
visit country_users_path(country)
is_expected.to have_content "some content on the page"
end
end
The specs all require javascript (I don't know whether that is important here).
Authentication is handled by Devise, and my rails_helper.rb includes
config.append_after(:each) do
DatabaseCleaner.clean
Warden.test_reset!
end
Why does rspec think the user is not signed in when all specs are run together, yet each spec passes individually?
It took a long time to get to the bottom of this. Posting this hear in case it is of help to anyone else encountering the same issue.
After much searching I eventually found this small mention that login_as may not work with Poltergeist when js is enabled on your test scenarios.
I tried the suggested fix to deal with shared DB connections. Unfortunately this resulted in the following errors:
PG::DuplicatePstatement at /session/users/signin
ERROR: prepared statement "a1" already exists
I tried using the Transactional Capybara gem, but this did not seem to work well with Poltergeist.
Eventually I abandonned login_as completely, and instead wrote a short method that visits the login page, fills in email and password, and logs in that way.
This solution appears to be working. It adds a little overhead, so I'm only using it for tests with JS.
If you are using Capybara gem then there is no need to use :js with test cases
What I did if this helps-
scenario "visit with user signed in" do
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
visit "/"
expect(current_path).to eq('/')
expect(page).to have_title "Some Random Title"
end
The other way you can login user using feature specs like-
feature 'User signs in' do
before :each do
#user = FactoryGirl.create(:user)
end
scenario "Signing in with correct credentials" do
visit "/"
fill_in "Email", with: #user.email
fill_in "Password", with: #user.password
click_button "Log In"
expect(current_path).to eq("/login/useremail/verification")
expect(page).to have_content "Signed in successfully"
end
end
If your pages are ajax then refer to this https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara
I currently have a test that doesn't seen to have the ability to visit particular paths. This test is a request spec and this is my first one, as far as I can tell request spec are similar to feature test and can user capybara to do things like visit and fill_in. But right now I can't get this request spec to even visit a path. Is there something I should know about request specs? I'll post my code and see if you see anything strange?
SPEC:
require "spec_helper"
describe "Mailchimp" do
describe "Manage list" do
it "adds new subscriber to list" do
VCR.use_cassette "mailchimp/subscriber" do
visit new_subscriber_path
expect {
fill_in "first_name", with: "John"
fill_in "last_name", with: "Mayer"
fill_in "phone_number", with: "61615551233"
fill_in "email", with: "john#rowster.com"
click_button "Sign Up"
}.to change(Subscriber, :count).by(1)
end
end
end
end
Let me know if you need to see anything else. Thank You!
Request specs used to be the same thing as feature specs in earlier versions of RSpec, but things have since changed.
Request specs are designed for you to hit the full stack via an HTTP request and inspect details from the response. You use methods like get, post, patch, and delete to interact with your application.
An example request spec:
get "/users/#{user.id}"
expect(response.body).to include user.full_name
Feature specs are driven by Capybara and allow you to hit the full stack via elements of the interface. If you want to hit a specific URL, that's when you use visit.
Your question includes an example of a feature spec, so I don't really need to echo it in this answer. My piece of advice related to your code would be to change it so that it's inspecting the interface and not how it changes the database.
(So I guess I will echo your code after all. :))
require "rails_helper"
feature "Mailchimp" do
describe "Manage list" do
scenario "adds new subscriber to list" do
VCR.use_cassette "mailchimp/subscriber" do
visit new_subscriber_path
fill_in "first_name", with: "John"
fill_in "last_name", with: "Mayer"
fill_in "phone_number", with: "61615551233"
fill_in "email", with: "john#rowster.com"
click_button "Sign Up"
expect(page).to have_content "You have successfully subscribed to our service"
end
end
end
end
I'm trying to test devise sign in, sign out and all the other scenarios, however I cannot get a single scenario to past, lets take login failure
in my feature I have
scenario 'user cannot sign in if not registered' do
login_as('user2#example.com', 'meow')
visit overviews_path
save_and_open_page
expect(page).to have_content I18n.t 'devise.failure.not_found_in_database', authentication_keys: 'email'
end
I also have the sign_in helper setup as;
def sign_in(email, password)
user.confirm!
visit new_user_session_path
fill_in 'Email', with: email
fill_in 'password', with: password
click_button 'Log in'
end
however this create an error;
expected to find text "Invalid email or password." in "TypeError at /overviews ======================= > no implicit conversion of Symbol into Integer spec/features/users/sign_in_spec.rb, line 14
any ideas?
You named your helper method sign_in but you're calling login_as in your scenario. You should use one approach or the other, not both.
UPDATE: OK, rechecked the documentation, and you should either use your own helper so that you're emulating an actual user signing in, or the login_as provided by Warden, in which case make sure you're included this in your tests / rspec setup:
include Warden::Test::Helpers
Warden.test_mode!
On a side note, you should confirm your user in your factory/fixture, not in your helper method (where it's probably not defined).
Im learning Rails 4 and and trying to write some tests using Rspec and capybara. Im writing a feature test for my users and I'm trying to test a user signing in.
feature "User" do
scenario "A user signs in" do
let(:user) { FactoryGirl.create(:user) }
visit signin_path
fill_in "Username", with: user.username
fill_in "Password", with: "123456"
click_button "Log in"
expect(page).to have_content(user.username)
end
end
Its telling me that let is an undefined method. and I'm sure that the problem is that it is in a feature/scenario test. How do I define the user in this kind of test?
Also, in a describe/it request test like this
describe "Something" do
it "should do something" do
expect(page).to have_content{"...")
end
end
I can shorten it like this
describe "Something" do
subject { page }
it { should have_content("...") }
end
Is there a way to shorten the expect(page)..... in a scenario? Thanks.
let is used for lazy-initialization of "variables" as needed across multiple tests; initializing it in a test is nonsensical. Either move the let outside of the scenario block, or just use standard variable assignment, like user = FactoryGirl.create(:user).
I am working on tests but running in to a road block on pages that require a current_user. I am using minitest, capybara, factorygirl, and authlogic, in rails 3.2.9 with ruby 1.9.3p327. I installed minitest as a separate gem, and seem to have the test environment working correctly.
I have a factory that creates a valid user...I call that factory from in a test like this:
describe "UsersAcceptanceTest" do
it "must load and include content" do
FactoryGirl.create(:user)
visit users_path
page.must_have_content("cPanel")
end
end
The FAIL is correct in informing me that the content "cPanel" was not found (cPanel is a link available to logged in users). The fail error goes on to alert me that it was not found in "log in, forgot password, contact" ... which of course means that the test routed correctly to users_path, but was redirected by authlogic because the user is not logged in. Users cant create themselves in my system and therefor are not auto-logged in on create.
How to I also get the factory to create a new user session with the newly created user?
You can do it this way:
visit signin_path
fill_in 'email', with: user.email
fill_in 'password', with: user.password
click_button "Log in"
Just edit it according to your login page structure.
I don't know about minitest, but in rspec I'd create the separate method with this codedef sign_in...end and put it to support\utilities.rb.
Then your code would be looking like that:
describe "UsersAcceptanceTest" do
let(:user) { Factory(:user) }
subject { page }
it "must load and include content" do
sign_in user
visit users_path
it { should have_link("cPanel", href: cpanel_path) }
end
end
As you can see I've edited your code a little bit more.