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
Related
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 am using rails and want to write a test for password reset in Rspec. i am quite new to testing.
this is what i have done so far:
require 'rails_helper'
describe UsersController, type: :controller do
describe 'post #reset_password' do
let(:user) { create(:user) }
context "reset password" do
def do_request
patch :update_password
end
before { do_request }
it { expect(ActionMailer::Base.deliveries.count(1) }
end
end
end
every time i run this it gives ma an syntax error in
"it { expect(ActionMailer::Base.deliveries.count(1) } ".
i want to check whether the email successfully sent of not and if the user have key in the email.
Thanks!
1) you miss ) at last here so got syntax error
it { expect(ActionMailer::Base.deliveries.count(1) }
to
it { expect(ActionMailer::Base.deliveries.count(1)) }
2)
If you want to check total deliveries. you can try
it 'should send an email' do
ActionMailer::Base.deliveries.count.should == 1
end
also check sender
it 'renders the sender email' do
ActionMailer::Base.deliveries.first.from.should == ['notifications#domain.com']
end
Also check subject line
it 'should set the subject to the correct subject' do
ActionMailer::Base.deliveries.first.subject.should == 'Here Is Your Story!'
end
The problems you're having will most likely be fixed by writing better tests.
Here's generally how you would write tests for something like this.
Lets suppose in your routes file you have a post route that looks something like this
# config/routes.rb
post "/user/:id/reset_password", to: "users#reset_password"
And your User controller looks something like this
# app/controllers/users_controller.rb
class UsersController
...
def reset_password
user = User.find(params[:id])
user.reset_password!
SomeMailClass.email_reset_instructions(user)
end
end
and your User.rb model looks something like this
# app/models/user.rb
class User < ActiveRecord::Base
def reset_password!
update!(password: nil) # or whatever way you want/need to reset the password
end
end
and you have some type of mailing class to send your email
# app/models/some_mail_class.rb
class SomeMailClass
def self.email_reset_instructions(user)
# do something to send email...
end
end
The way you would go about testing this in the controller would be
# spec/controllers/users_controller_spec.rb
require 'rails_helper'
describe UsersController, type: :controller do
it "#reset_password" do
user_id = double(:user_id)
user = double(:user)
expect(User).to receive(:find).with(user_id).and_return(user)
expect(user).to receive(:reset_password!).and_return(true)
expect(SomeMailClass).to receive(:email_reset_instructions).with(user)
post :reset_password, id: user_id
end
end
But you shouldn't stop there. Because the implementation of the newly made method reset_password! and the SomeMailClass has yet to be tested. So you would write model/unit tests like this for them
# spec/models/user_spec.rb
require "rails_helper"
describe User do
it ".reset_password!" do
user = User.create(password: "foo")
expect(user.password).to eq "foo"
user.reset_password!
expect(user.password).to eq nil
end
end
Then you might install vcr and factory_girl gems and use them like so to test your mailer
# spec/models/some_mail_class_spec.rb
require "rails_helper"
describe SomeMailClass do
VCR.use_cassette "email_reset_instructions" do |cassette|
it ".email_reset_instructions" do
user = FactoryGirl.create(:user)
SomeMailClass.email_reset_instructions(user)
# you can write some expectations on the cassette obj to test.
# or you can write whatever expectations you need/desire
end
end
end
And in the end if there was something happening on the front end that a user would click that made this post request you would write a feature test for it as well.
Hope this helps!
Following instructions from that Devise How-To page I'm trying to rebuild all my rspec test to bypass user signin process.
There are 2 methods that can be used for that:
sign_in from Devise - which can't be used with feature tests (Capybara)
login_as from Warden (Devise is build on top of it)
1st method at the first shot worked (all test passes) except those with Capybara so I decided to leave it for now.
2nd gives me some weird results - all passes except the first one (any which I place as the first in the file). It fails when I run just one of them. I've checked it with binding.pry and it fails as the user is not logged in and it redirects to the login page. Somehow first test triggers something that makes all the rest passing. I have no clue what's going on here.
I was using around hook before but it behaves really weird so I've changed that to set of before and after (at the same time it works much faster as it creates just one user at the begging rather than for every test). This is how it looks like now:
require 'spec_helper'
describe AlbumsController do
let(:album) { create(:album) }
before(:all) do
#user = create :user
end
before(:each) do
login_and_switch_schema #user
end
after(:all) do
destroy_users_schema #user
destroy_user #user
end
describe "GET #new" do
before { get :new }
it { expect(response).to render_template :new }
end
describe "GET #edit" do
before { get :edit, id: album }
it { expect(response).to render_template :edit }
end
...
and I've defined that helpers:
Warden.test_mode!
def login_and_switch_schema(user)
##request.env["devise.mapping"] = Devise.mappings[:user]
#sign_in :user, user
login_as(user, scope: :user)
Apartment::Database.switch(user.username)
end
def destroy_users_schema(user)
Apartment::Database.drop(user.username)
Apartment::Database.reset
end
def destroy_user(user)
User.destroy(user)
end
I would like to ask you for help.
I would try moving your before(:all) and after(:all) code into the before(:each) and after(:each). :all doesn't play well with let, DatabaseCleaner or in giving you a predictable order of execution for the first test executed.
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!
I have started my journey with TDD in Rails and have run into a small issue regarding tests for model validations that I can't seem to find a solution to. Let's say I have a User model,
class User < ActiveRecord::Base
validates :username, :presence => true
end
and a simple test
it "should require a username" do
User.new(:username => "").should_not be_valid
end
This correctly tests the presence validation, but what if I want to be more specific? For example, testing full_messages on the errors object..
it "should require a username" do
user = User.create(:username => "")
user.errors[:username].should ~= /can't be blank/
end
My concern about the initial attempt (using should_not be_valid) is that RSpec won't produce a descriptive error message. It simply says "expected valid? to return false, got true." However, the second test example has a minor drawback: it uses the create method instead of the new method in order to get at the errors object.
I would like my tests to be more specific about what they're testing, but at the same time not have to touch a database.
Anyone have any input?
CONGRATULATIONS on you endeavor into TDD with ROR I promise once you get going you will not look back.
The simplest quick and dirty solution will be to generate a new valid model before each of your tests like this:
before(:each) do
#user = User.new
#user.username = "a valid username"
end
BUT what I suggest is you set up factories for all your models that will generate a valid model for you automatically and then you can muddle with individual attributes and see if your validation. I like to use FactoryGirl for this:
Basically once you get set up your test would look something like this:
it "should have valid factory" do
FactoryGirl.build(:user).should be_valid
end
it "should require a username" do
FactoryGirl.build(:user, :username => "").should_not be_valid
end
Here is a good railscast that explains it all better than me:
UPDATE: As of version 3.0 the syntax for factory girl has changed. I have amended my sample code to reflect this.
An easier way to test model validations (and a lot more of active-record) is to use a gem like shoulda or remarkable.
They will allow to the test as follows:
describe User
it { should validate_presence_of :name }
end
Try this:
it "should require a username" do
user = User.create(:username => "")
user.valid?
user.errors.should have_key(:username)
end
in new version rspec, you should use expect instead should, otherwise you'll get warning:
it "should have valid factory" do
expect(FactoryGirl.build(:user)).to be_valid
end
it "should require a username" do
expect(FactoryGirl.build(:user, :username => "")).not_to be_valid
end
I have traditionally handled error content specs in feature or request specs. So, for instance, I have a similar spec which I'll condense below:
Feature Spec Example
before(:each) { visit_order_path }
scenario 'with invalid (empty) description' , :js => :true do
add_empty_task #this line is defined in my spec_helper
expect(page).to have_content("can't be blank")
So then, I have my model spec testing whether something is valid, but then my feature spec which tests the exact output of the error message. FYI, these feature specs require Capybara which can be found here.
Like #nathanvda said, I would take advantage of Thoughtbot's Shoulda Matchers gem. With that rocking, you can write your test in the following manner as to test for presence, as well as any custom error message.
RSpec.describe User do
describe 'User validations' do
let(:message) { "I pitty da foo who dont enter a name" }
it 'validates presence and message' do
is_expected.to validate_presence_of(:name).
with_message message
end
# shorthand syntax:
it { is_expected.to validate_presence_of(:name).with_message message }
end
end
A little late to the party here, but if you don't want to add shoulda matchers, this should work with rspec-rails and factorybot:
# ./spec/factories/user.rb
FactoryBot.define do
factory :user do
sequence(:username) { |n| "user_#{n}" }
end
end
# ./spec/models/user_spec.rb
describe User, type: :model do
context 'without a username' do
let(:user) { create :user, username: nil }
it "should NOT be valid with a username error" do
expect(user).not_to be_valid
expect(user.errors).to have_key(:username)
end
end
end