The following is my test and everything works. The form gets filled with a name and image in the ui when I run the test. If I also test the presence of the user's name being created in the ui when I visit groups_path the test also passes. The only issue is that a group is not getting created. I'm not sure what I'm missing.
I'm using devise for authentication
create_group_spec.rb
require 'rails_helper'
RSpec.describe 'Creating a group', type: :feature do
before :each do
user = User.create!(name: 'Test user', email: 'test#gmail.com', password: '123456',
password_confirmation: '123456')
login_as(user, scope: :user)
end
scenario 'adds a new group when name and image is entered' do
visit new_group_path
fill_in('Name', with: 'Sports')
attach_file('Icon', "#{Rails.root}/integration_test_image/test.jpg")
sleep(3)
click_on 'Create Group'
visit groups_path
expect(page).to have_content('Sports')
end
end
groups_controller.rb
def create
#group = Group.new(group_params)
#group.user = current_user
respond_to do |format|
if #group.save
format.html { redirect_to groups_url, notice: 'Group was successfully created.' }
format.json { render :show, status: :created, location: #group }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #group.errors, status: :unprocessable_entity }
end
end
end
private
def group_params
params.require(:group).permit(:name, :icon)
end
No more sleeping on the job. sleep x doesn't actually guarentee that whatever your waiting for is actually finished - it just makes your tests slower and potentially flappy.
require 'rails_helper'
RSpec.describe 'Creating a group', type: :feature do
before :each do
# You should be using fixtures or factories instead.
user = User.create!(name: 'Test user', email: 'test#gmail.com', password: '123456',
password_confirmation: '123456')
login_as(user, scope: :user)
end
scenario 'adds a new group when name and image is entered' do
visit new_group_path
fill_in('Name', with: 'Sports')
# use a block instead of sleep
# images should also go in /spec/support or /spec/fixtures
attach_file('Icon', Rails.root.join("/integration_test_image/test.jpg")) do
click_on 'Create Group' # no need to visit since it will redirect
expect(page).to have_content('Sports') # have_content waits for the page to load
end
end
end
I fixed it by adding sleep(1) after the click_on 'Create Group' line. I think by visiting the groups_path it happens too fast even before the group object creation is complete hence it becomes unavailable in the ui and is the reason for the test failing. Delaying the switch to the page by 1s ensures the object completes being created and is available when the page is rendered hence the test passing.
Related
I've got a simple message app to learn RSpec where one user can create message to another user (only logged users can write messages). I didn't used devise to log in or sign up users, this app is as simple as possible just for rspec learning.
I want to test create method in sessions controller:
class SessionsController < ApplicationController
before_action :logged_in_redirect, only: %i[new create]
def new; end
def create
user = User.find_by(username: params[:session][:username])
if user && user.authenticate(params[:session][:password])
session[:user_id] = user.id
flash[:success] = 'You have successfully logged in'
redirect_to root_path
else
flash.now[:error] = 'There was something wrong with your login'
render 'new'
end
end
def logged_in_redirect
if logged_in?
flash[:error] = 'You are already logged in'
redirect_to root_path
end
end
This is relevant code from my sessions controller spec:
RSpec.describe SessionsController, type: :controller do
describe 'POST #create' do
context 'when password is correct' do
let(:user) { User.create(username: 'John', password: 'test123') }
it 'redirect to root path' do
post :create, session: { username: 'John', password: 'test123' }
expect(response).to have_http_status(:redirect)
end
end
end
end
The error says undefined method `[]' for nil:NilClass. It works as expected in the application. What am I missing? Should I use session[:user_id] after let(:user) ?
Use let to define a memoized helper method. The value will be cached
across multiple calls in the same example but not across examples.
Note that let is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked. You can use let! to
force the
- List item
method's invocation before each example.
xou can refer this to get further detailsrelish docs
Possibility 1
RSpec.describe SessionsController, type: :controller do
describe 'POST #create' do
before :each do
#user = User.create(username: 'John', password: 'test123')
end
context 'when password is correct' do
it 'redirect to root path' do
post :create, params: {session: { username: #user.username, password: #user.password }}
expect(response).to have_http_status(:redirect)
end
end
end
end
Possibility 2
RSpec.describe SessionsController, type: :controller do
let(:create_user) do
#user = User.create(username: 'John', password: 'test123')
end
describe 'POST #create' do
context 'when password is correct' do
it 'redirect to root path' do
create_user
post :create, params: {session: { username: #user.username, password: #user.password }}
expect(response).to have_http_status(:redirect)
end
end
end
end
Can anyone see why my put spec for my controller wont pass?
Here is what my update action in my controller looks like:
def update
#user = User.find(current_user.id)
respond_to do |format|
if #user.update_attributes(permitted_update_params)
format.html { redirect_to new_topup_path, notice: 'Billing address was succesfully updated' }
format.json { respond_with_bip(#user) }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
And my spec looks like this:
context "given a user who wants to update their billing address" do
let!(:user) { create(:user, billing_address: "My initial address") }
before(:each) do
allow(controller).to receive(:current_user) {:user}
patch :update, { user: { :billing_address => "My Second address" } }
end
it "should update the users billing address" do
expect(user.billing_address).to eq("My Second address")
end
end
My spec displays this message:
Failure/Error: expect(user.billing_address).to eq("My Second address")
expected: "My Second address"
got: "My initial address"
You likely need to reload the user instance in your test. The database has been updated, but the user instance won't update itself to reflect that.
expect(user.reload.billing_address).to eq("My Second address")
There are also other problems with your code, like this line:
allow(controller).to receive(:current_user) {:user}
You've defined a user with let(:user), which means you now have a user variable available to your spec - note user, not :user!
You should reload your user before expectation:
before(:each) do
allow(controller).to receive(:current_user) {:user}
patch :update, { user: { :billing_address => "My Second address" } }
user.reload
end
A controller spec should test the behavior of the action. Your action's behavior can be roughly described as:
Get the permitted parameters
Update the user
If the update succeeded, redirect
Otherwise redisplay the edit page
Updating the user is the responsibility of the model, not the controller. If you are concerned that a particular set of parameters will (or won't) update the user instance, create a model spec and test the parameters there.
My personal website is being built on rails but I'm stuck with the User.count for the login area not updating. Been sitting with this for a day trying to get it to work but no luck so far. I've included all the code if someone could spot my error as I can't see it.
class UsersControllerTest < ActionController::TestCase
setup do
#user = users(:one)
#input_attributes = {
name: 'luchia',
password: 'secret',
password_confirmation: 'secret'
}
end
test "should create user" do
assert_difference('User.count') do
post :create, user: #input_attributes
end
assert_redirected_to users_path
end
Then there is my User Controller
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to users_url, notice: "User #{#user.name} was successfully created." }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
And my Terminal error:
$ rake test
Run options: --seed 11633
# Running tests:
....[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
....................F......
Finished tests in 1.143021s, 27.1211 tests/s, 45.4935 assertions/s.
1) Failure:
UsersControllerTest#test_should_create_user [/Users/lucybloomfield/Documents/luchia /test/controllers/users_controller_test.rb:25]:
"User.count" didn't change by 1.
Expected: 3
Actual: 2
31 tests, 52 assertions, 1 failures, 0 errors, 0 skips
Would appreciate anyone's help.
////////////////
Mmkay, this is what I ended up coming up with but it's not very concise. Rake test is completing with this so I guess it will have to do.
test "should create user" do
assert_difference('User.count') do
post :create, {:user => {'name' => 'luchia', 'password' => 'secret', 'password_confirmation' => 'secret'}}
.to change(User, :count).by(1)
end
assert_redirected_to users_path
end
//////////////
Edit again, the above bit of code fails on the second rake test.
Example with capybara and cucumber
describe "new" do
before do
visit new_user_path
end
it { should have_title(I18n.t("user.new")) }
describe "with invalid information" do
before { find("form#new_user").submit_form! }
it { should have_title(I18n.t("user.new")) }
it { should have_selector("div#alerts") }
end
describe "with valid information" do
before do
fill_in "user_name", :with => "user_name"
fill_in "user_email", :with => "user#example.com"
fill_in "user_password", :with => "123456"
fill_in "user_password_confirmation", :with => "123456"
find("form#new_user").submit_form!
end
it { should have_content(I18n.t("user.success"))}
end
end
You have to do a
puts YAML::dump(#user.save!)
in your method create of UsersControlller
def create
#user = User.new(user_params)
puts YAML::dump(#user.save!)
...
end
To see why it doesn't save.
Then check your user_params permit if it necessary
set
I18n.enforce_available_locales = false
to your config/application.rb
inside as exampled:
module MyApp
class Application < Rails::Application
...
I18n.enforce_available_locales = false
end
end
and restart server
When running my tests, javascript isn't getting executed. I tried switching from the :selenium driver to the :webkit driver, but still no luck.
It works fine in the browser. When the test runs and the page opens, the page is blank
Is there something else I need to do to get it to execute javascript during test runs?
business_integration_test.rb
require 'minitest_helper'
require 'ruby-debug'
describe "Business integration" do
before :each do
#business = Factory.build(:business)
Capybara.javascript_driver = :webkit
end
it "Should show error if business name is blank" do
visit admin_business_path
fill_in 'Name', :with => ''
click_button 'Save'
page.save_and_open_page
page.text.must_include("can't be blank")
end
end
businesses_controller.rb
class Admin::BusinessesController < Admin::AdminController
def update
#business = resource
respond_to do |format|
if #business.update_attributes(params[:business])
flash[:notice] = "Your business was updated successfully."
else
flash[:notice] = "Your business failed to update."
end
format.js { render :template => "admin/businesses/edit" }
end
end
end
edit.js.erb
$("#page-businesses #main").html("<%= escape_javascript(render :partial => 'edit_information') %>");
I'm writing an rspec scenario thats failing with:
(#<User:0x1056904f0>).update_attributes(#<RSpec::Mocks::ArgumentMatchers::AnyArgMatcher:0x105623648>)
expected: 1 time
received: 0 times
users_controller_spec.rb:
describe "Authenticated examples" do
before(:each) do
activate_authlogic
#user = Factory.create(:valid_user)
UserSession.create(#user)
end
describe "PUT update" do
it "updates the requested user" do
User.stub!(:current_user).and_return(#user)
#user.should_receive(:update_attributes).with(anything()).and_return(true)
put :update, :id => #user , :current_user => {'email' => 'Trippy'}
puts "Spec Object Id : " + "#{#user.object_id}"
end
users_controller.rb:
def update
#user = current_user
puts "Controller Object ID is : " + "#{#user.object_id}"
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to(root_url, :notice => 'Successfully updated profile.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
user.rb - factories
Factory.define :valid_user, :class => User do |u|
u.username "Trippy"
u.password "password"
u.password_confirmation "password"
u.email "elephant#gmail.com"
u.single_access_token "k3cFzLIQnZ4MHRmJvJzg"
u.id "37"
end
Authlogic's standard helper methods like current_user don't call User.find directly. I believe it does current_user_session.user, where current_user_session calls UserSession.find, so you're not calling User.find directly. You could do some fancy chain stubbing there, but my suggestion is just to add this to your controller spec instead of what you're currently stubbing:
stub!(:current_user).and_return(#user)
In RSpec2 you might have to do
controller.stub!(:current_user).and_return(#user)
Edit: This should be your whole spec file:
describe "Authenticated examples" do
before(:each) do
activate_authlogic
#user = Factory.create(:valid_user)
UserSession.create(#user)
end
describe "PUT update" do
describe "with valid params" do
it "updates the requested user" do
stub!(:current_user).and_return(#user)
#user.should_receive(:update_attributes).with(anything()).and_return(true)
put :update, :id => #user , :current_user => {'email' => 'Trippy'}
end
end
I think you're confusing stubs with message expectations. The line
User.should_receive(:find)
tells Rspec to expect the User model to receive a find message. Whereas:
User.stub!(:find)
replaces the find method so that the test can pass. In your example the thing you're testing is whether update_attributes is called successfully, so that ought to be where the message expectation goes, and the job of all the other testing code is just to set up the prerequisites.
Try replacing that line with:
User.stub!(:find).and_return(#user)
Note that find returns the object, not just its id. Also, note that stubbing out find here serves only to speed things up. As written the example gets through should_receive(:find) successfully, and that is happening because you're using Factories to create users in the test database. You could take the stub out and the test should still work, but at the cost of hitting the database.
Another tip: if you're trying to figure out why a controller test isn't working, sometimes it's helpful to know if it is being blocked by before filters. You can check for this with:
controller.should_receive(:update)
If that fails, the update action is not being reached, probably because a before filter has redirected the request.