I'm making an app where people can create jobs, but can only destroy the jobs they've created. I know that my app lets people destroy jobs because I can test it manually, but RSpec is lagging behind.
Here's the relevant test:
jobs_controller_spec.rb
require 'spec_helper'
describe JobsController do
let!(:user) { FactoryGirl.create(:user) }
let!(:job) { FactoryGirl.create(:job, user: user) }
let!(:wrong_user) { FactoryGirl.create(:user, email: "wrong#example.com") }
let!(:wrong_job) { FactoryGirl.create(:job, user: wrong_user) }
[...]
describe "correct user control" do
before { sign_in user }
describe "users can only delete their own jobs" do
it "should not change job count" do
expect do
delete :destroy, id: wrong_job.id
end.to_not change(Job, :count)
end
end
describe "users can delete their own jobs" do
it "should decrease job count" do
expect do
delete :destroy, id: job.id
end.to change(Job, :count).by(-1)
end
end
end
end
Here's the failing test:
1) JobsController correct user control users can delete their own jobs should decrease job count
Failure/Error: expect do
count should have been changed by -1, but was changed by 0
# ./spec/controllers/jobs_controller_spec.rb:41:in `block (4 levels) in <top (required)>'
jobs_controller.rb
class JobsController < ApplicationController
skip_before_action :require_signin, only: [:index, :show]
skip_before_action :correct_user, only: [:index, :show, :new, :create]
before_action :set_job, only: [:show, :edit, :update, :destroy]
[...]
def destroy
#job.destroy
respond_to do |format|
format.html { redirect_to jobs_url }
format.json { head :no_content }
end
end
private
def set_job
#job = Job.find(params[:id])
end
def job_params
params.require(:job).permit(:title, :org, :internship, :postdate, :filldate, :location, :link, :description)
end
end
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_filter :require_signin
before_filter :correct_user
include SessionsHelper
private
def require_signin
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
def correct_user
#job = current_user.jobs.find_by(id: params[:id])
redirect_to root_url if #job.nil?
end
end
rake routes
Prefix Verb URI Pattern Controller#Action
jobs GET /jobs(.:format) jobs#index
POST /jobs(.:format) jobs#create
new_job GET /jobs/new(.:format) jobs#new
edit_job GET /jobs/:id/edit(.:format) jobs#edit
job GET /jobs/:id(.:format) jobs#show
PATCH /jobs/:id(.:format) jobs#update
PUT /jobs/:id(.:format) jobs#update
DELETE /jobs/:id(.:format) jobs#destroy
[...]
As Peter Alfvin points out, if there is authentication in controller specs you have to setup a session and pass it as third parameter, for example:
...
let(:some_user) { User.create }
def valid_session
{ user_id: some_user.id }
end
describe "DELETE destroy" do
it "destroys the requested job" do
job = Job.create! valid_attributes
expect {
delete :destroy, { :id => job.to_param }, valid_session
}.to change(Job, :count).by(-1)
end
end
The solution is to pass no_capybara: true to sign_in. Capybara doesn't work with controller tests, so one can't use capybara to manage the sign in process.
Thanks to Patrick Brinich-Langlois for the solution.
Related
Background
I have created a movie review app that allows a logged in user to add and edit a movie as well as leave a review for each movie.
Problem
I am working on testing my controllers, however I keep getting:
3) Error:
ReviewsControllerTest#test_should_get_edit:
ActionController::UrlGenerationError: No route matches
{:action=>"edit_movie_review", :controller=>"reviews"}
test/controllers/reviews_controller_test.rb:34:in `block in <class:ReviewsControllerTest>'
4) Error:
ReviewsControllerTest#test_should_get_new:
ActionController::UrlGenerationError: No route matches {:action=>"new", :controller=>"reviews"}
test/controllers/reviews_controller_test.rb:17:in `block in <class:ReviewsControllerTest>'
5) Error:
ReviewsControllerTest#test_should_show_review:
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"reviews", :id=>"1"}
test/controllers/reviews_controller_test.rb:29:in `block in <class:ReviewsControllerTest>'
6) Error:
ReviewsControllerTest#test_should_create_review:
ActionController::UrlGenerationError: No route matches {:action=>"create", :controller=>"reviews", :review=>{:comment=>"MyText", :rating=>"1"}}
test/controllers/reviews_controller_test.rb:23:in `block (2 levels) in <class:ReviewsControllerTest>'
test/controllers/reviews_controller_test.rb:22:in `block in <class:ReviewsControllerTest>'
Rake routes:
POST /movies/:movie_id/reviews(.:format) reviews#create
new_movie_review GET /movies/:movie_id/reviews/new(.:format) reviews#new
edit_movie_review GET /movies/:movie_id/reviews/:id/edit(.:format) reviews#edit
movie_review GET /movies/:movie_id/reviews/:id(.:format) reviews#show
PATCH /movies/:movie_id/reviews/:id(.:format) reviews#update
PUT /movies/:movie_id/reviews/:id(.:format) reviews#update
DELETE /movies/:movie_id/reviews/:id(.:format) reviews#destroy
movies GET /movies(.:format) movies#index
POST /movies(.:format) movies#create
new_movie GET /movies/new(.:format) movies#new
edit_movie GET /movies/:id/edit(.:format) movies#edit
movie GET /movies/:id(.:format) movies#show
PATCH /movies/:id(.:format) movies#update
PUT /movies/:id(.:format) movies#update
ReviewsControllerTest:
require 'test_helper'
class ReviewsControllerTest < ActionController::TestCase
setup do
#review = reviews(:one)
#user = users(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:review)
end
test "should get new" do
get :new
assert_response :success
end
test "should create review" do
assert_difference('Review.count') do
post :create, review: { comment: #review.comment, rating: #review.rating }
end
assert_redirected_to review_path(assigns(:review))
end
test "should show review" do
get :show, id: #review
assert_response :success
end
test "should get edit" do
get :edit_movie_review, #review
assert_response :success
end
test "should update review" do
put :update
assert_redirected_to review_path(assigns(:review))
end
test "should destroy review" do
assert_difference('Review.count', -1) do
delete :destroy, id: #review
end
assert_redirected_to reviews_path
end
end
ReviewsController.rb:
class ReviewsController < ApplicationController
before_action :find_movie
before_action :find_review, only: [:edit, :update, :destroy]
before_action :authenticate_user!, only: [:new, :edit]
def new
#review = Review.new
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
#review.movie_id = #movie.id
if #review.save #if a review is succesfully saved, redirect user to home
redirect_to movie_path(#movie)
else
render 'new'
end
end
def edit
end
def update
if #review.update(review_params)
redirect_to movie_path(#movie)
else
render 'edit'
end
end
def destroy
#review.destroy
redirect_to movie_path(#movie)
end
private
def review_params
params.require(:review).permit(:rating, :comment)
end
def find_movie
#movie = Movie.find(params[:movie_id])
end
def find_review
#review = Review.find(params[:id])
end
end
I am new to the world of programming therefore any form of advice is much appreciated.
Thanks in advance!
You can pass movie_id with your routes. Try this for your new action test and you can follow the same pattern for other actions as well.
Note: You ReviewsController does not have index and show action.
require 'test_helper'
class ReviewsControllerTest < ActionController::TestCase
setup do
#movie = movies(:one)
#review = reviews(:one)
#user = users(:one)
end
#test "should get index" do
# get :index
# assert_response :success
# assert_not_nil assigns(:review)
#end
test "should get new" do
get :new, movie_id: #movie.id
assert_response :success
end
test "should create review" do
assert_difference('Review.count') do
post :create, movie_id: #movie.id ,review: { comment: #review.comment, rating: #review.rating }
end
assert_redirected_to movie_path(assigns(:review))
end
test "should get edit" do
get :edit, movie_id: #movie.id, id: #review.id
assert_response :success
end
test "should update review" do
put :update, movie_id: #movie.id, id: #review.id, review: { comment: #review.comment, rating: #review.rating }
assert_redirected_to movie_path(assigns(:review))
end
test "should destroy review" do
assert_difference('Review.count', -1) do
delete :destroy, id: #review, movie_id: #movie.id
end
end
end
This is my first foray into app development, and I have been following Michael Hartl's tutorial pretty much verbatim. All went fine until towards the end of chapter 12, when my tests started failing. The strange thing is that the tests which fail seem to be unrelated to the changes I made in chapter 12.
There are three failures and one error, and they are also duplicated by the local server when manually checked in the browser. Basically the logged_in_user before action doesn't seem to work, but only with some of the model actions, not all. I've tried to trace it with the debugger and tried to revert back to pre-chapter 12 commits (which all definitely worked) but I'm having trouble with database migrations and stuff and only introducing more and more errors. Below is the error log, my users controller, application controller and sessions helper. I feel like the error must be in one of those.
Many many thanks in advance for your help!
FAIL["test_should_redirect_update_when_not_logged_in", UsersControllerTest, 2016-03-06 05:58:45 -0800]
test_should_redirect_update_when_not_logged_in#UsersControllerTest
(1457272725.92s)
Expected true to be nil or false
test/controllers/users_controller_test.rb:27:in `block in '
ERROR["test_should_redirect_destroy_when_not_logged_in", UsersControllerTest, 2016-03-06 05:58:45 -0800]
test_should_redirect_destroy_when_not_logged_in#UsersControllerTest
(1457272725.96s) NoMethodError: NoMethodError: undefined
method admin?' for nil:NilClass
app/controllers/users_controller.rb:77:inadmin_user'
test/controllers/users_controller_test.rb:47:in block (2 levels) in <class:UsersControllerTest>'
test/controllers/users_controller_test.rb:46:inblock in '
app/controllers/users_controller.rb:77:in admin_user'
test/controllers/users_controller_test.rb:47:inblock (2 levels) in '
test/controllers/users_controller_test.rb:46:in `block in '
FAIL["test_should_redirect_edit_when_not_logged_in", UsersControllerTest, 2016-03-06 05:58:46 -0800]
test_should_redirect_edit_when_not_logged_in#UsersControllerTest
(1457272726.03s)
Expected true to be nil or false
test/controllers/users_controller_test.rb:21:in `block in '
FAIL["test_successful_edit_with_friendly_forwarding", UsersEditTest, 2016-03-06 05:58:46 -0800]
test_successful_edit_with_friendly_forwarding#UsersEditTest
(1457272726.07s)
Expected response to be a redirect to <[removed link]/users/633107804/edit> but was a redirect to <[removed
link]/users/633107804>.
Expected "[removed link]users/633107804/edit" to be === "[removed link]/users/633107804".
test/integration/users_edit_test.rb:22:in `block in '
class UsersController < ApplicationController
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to_users_url
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def new
#user = User.new
end
def index
#users = User.paginate(page: params[:page])
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
And the application controller:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include SessionsHelper
private
#confirms a logged in user. Now placed in the application controller so both teh user controller and microposts controller can use it
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
And the sessions helper:
module SessionsHelper
#logs in the given user
def log_in(user)
session[:user_id] = user.id
end
# returns the current logged-in user (if any)
def current_user
if (user_id = session[:user_id])
#current_user ||= User.find_by(id: session[:user_id])
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
#current_user = user
end
end
end
# returns true if the user is logged in, and false otherwise
def logged_in?
!current_user.nil?
end
#Saves a user id and remember token digest to cookies
def remember(user)
user.remember
#Code to save cookies with 20 year expiry ("permanent") and "signed" so it can't be accessed by third party
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
# runs the forget method and removes remember tokens from cookies
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# logs out the given user
def log_out
forget(current_user)
session.delete(:user_id)
#current_user = nil
end
def current_user?(user)
user == current_user
end
# Redirects to stored location after trying to edit while logged out and then logging in
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# Stores the URL trying to be accessed
def store_location
session[:forwarding_url] = request.url if request.get?
end
end
Userscontrollertest:
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def setup
#user = users(:Joe)
#other_user = users(:Jane)
end
test "should redirect to index when not logged in" do
get :index
assert_redirected_to login_url
end
test "should get new" do
get :new
assert_response :success
end
test "should redirect edit when not logged in" do
get :edit, id: #user
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when not logged in" do
patch :update, id: #user, user: { name: #user.name, email: #user.email }
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect edit when logged in as wrong user" do
log_in_as(#other_user)
get :edit, id: #user
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect update when logged in as the wrong user" do
log_in_as(#other_user)
patch :update, id: #user, user: { name: #user.name, email: #user.email }
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'User.count' do
delete :destroy, id: #user
end
assert_redirected_to login_url
end
test "should redirect destroy when logged in as a non-admin" do
log_in_as(#other_user)
assert_no_difference 'User.count' do
delete :destroy, id: #user
end
assert_redirected_to root_url
end
end
After comparing your code to what I have in my tutorial code, try changing the order of the before_action statements in your UsersController and see if that works:
Before:
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
After:
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
All the before_action statements are run in the order they are defined in the controller, so for the update action, you're currently running the correct_user filter first, which calls current_user?(#user), which calls current_user:
def current_user
if (user_id = session[:user_id])
#current_user ||= User.find_by(id: session[:user_id])
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
#current_user = user
end
end
end
At that point in your tests, since you haven't logged in a user yet, you don't have a session[:user_id], so that if statement fails, and you also don't have a cookies.signed[:user_id], so the elsif statement fails, leaving you with a nil return value and #current_user not being set.
At some point in your request in the test, you seem to be finding its way over to the admin_user method, which is calling current_user.admin?, and since at this point current_user is nil, you're getting the NoMethodError: undefined method admin?' for nil:NilClass that you're seeing.
I am writing a rails application with devise and testing in rspec. I have an issue where my rspec fails the user_authenticate when the user is not logged in. All of my specs pass except for the last one- the error it gives is
"Failure/Error: get :show, id: course NoMethodError:undefined method `authenticate' for nil:NilClass"
I suspect I am having this issue because I have a before_action :authenticate_user! call and for someone not logged in, it tries to authenticate nil. Is there a way to make it fail gracefully and redirect to user_session? I tried to create an inherited version of authenticate_user to do the redirect, but it does not appear to work. I know this is probably a noob question but I have extensively searched around without any solution. thanks!
This is my controller:
class CoursesController < ApplicationController
before_action :authenticate_user!, except: [:index]
before_action :set_course, only: [:show]
def index
#course = Course.order('name')
end
def show
end
private
def set_course
#course = Course.find(params[:id])
end
def course_params
params.require(:course).permit(:name,:description,:department,:hidden,
:lecture_attributes => [:name,:description,:level])
end
def authenticate_user!
if user_signed_in?
super
else
redirect_to user_session
end
end
end
This is my spec:
require 'rails_helper'
RSpec.describe CoursesController, :type => :controller do
describe "user access " do
before(:each) do
#user = create(:user)
#request.env['devise.mapping'] = Devise.mappings[:user]
sign_in :user, #user
end
describe 'GET #index' do
it 'renders the :index view' do
get :index
expect(response).to render_template :index
end
end
describe 'GET #show' do
it 'assigns the requested course to #course' do
course = create(:course)
get :show, id: course
expect(assigns(:course)).to eq course
end
it 'renders the :show template' do
course = create(:course)
get :show, id: course
expect(response).to render_template :show
end
end
end
describe "guest access " do
describe 'GET #index' do
it 'renders the :index view' do
get :index
expect(response).to render_template :index
end
end
describe 'GET #show' do
it 'redirects to the login url' do
course = create(:course)
get :show, id: course
expect(response).to redirect_to 'user_session'
end
end
end
end
It seems that devise does the redirect to "users#sessions" itself when you add :authenticate_user! to the show action for a guest or a user that is not signed in.
Try removing your custom :authenticate_user! method and add "only: [:show]" to your before_action
class CoursesController < ApplicationController
before_action :authenticate_user!, only: [:show], except: [:index]
before_action :set_course, only: [:show]
def index
#course = Course.order('name')
end
def show
end
private
def set_course
#course = Course.find(params[:id])
end
def course_params
params.require(:course).permit(:name,:description,:department,:hidden,
:lecture_attributes => [:name,:description,:level])
end
end
Update
class CoursesController < ApplicationController
before_action :authenticate_user!, except: [:index]
before_action :set_course, only: [:show]
def index
#course = Course.order('name')
end
def show
if user_signed_in?
render :show
else
redirect_to user_session
end
end
private
def set_course
#course = Course.find(params[:id])
end
def course_params
params.require(:course).permit(:name,:description,:department,:hidden,
:lecture_attributes => [:name,:description,:level])
end
end
This isn't a super satisfying result but it appears as if authenticate_user! does not properly work with rspec. When I load the page directly, it correctly redirects to the login page, I am still interested to know what the proper work around is.
Can I have some sort of OR statement where I can first check if a user exists? There must be a standard way to deal with this problem so I can ensure my app is properly redirecting.
Hi everyone I'm testing my app controllers and I have a problem. I have tests for update action which fails:
describe "PUT #update" do
before :each do
#car_service = create(:car_service)
end
it "locates the requested #message" do
put :update, id: #car_service, car_addition: attributes_for(:car_service)
assigns(:car_addition).should eq(#car_service)
end
context "valid attributes" do
it "changes #car_service's attributes" do
put :update, id: #car_service, car_addition: attributes_for(:car_service, name: "Test")
#car_service.reload
#car_service.name.should eq("Test")
end
it "redirects to the updated message" do
put :update, id: #car_service, car_addition: attributes_for(:car_service)
should redirect_to admin_car_additions_url
end
end
context "invalid attributes" do
it "does not change #car_addition's attributes" do
put :update, id: #car_service, car_addition: attributes_for(:car_service, name: nil)
#car_service.reload
#car_service.name.should_not be_nil
end
it "re-renders the edit method" do
put :update, id: #car_service, car_addition: attributes_for(:car_addition)
should render_template :edit
end
end
end
when i run this tests only one test not pass("re-renders the edit method") and throw out following error:
Failure/Error: should render_template('edit')
expecting <"edit"> but rendering with <[]>
# ./spec/controllers/admin/car_additions_controller_spec.rb:100:in `block (4 levels) in <top (required)>
My controller looks like this:
module Admin
class CarAdditionsController < ApplicationController
include Admin::BaseController
load_and_authorize_resource
add_breadcrumb I18n.t('car_additions.car_addition.home'), :admin_root_path
add_breadcrumb I18n.t('car_additions.car_additions'), :admin_car_additions_path
def index
end
def new
add_breadcrumb t('car_additions.car_addition.new')
end
def edit
add_breadcrumb t('car_additions.car_addition.edit')
end
def create
if #car_addition.save
flash[:notice] = t("car_additions.created")
redirect_to action: :index
else
add_breadcrumb t('car_additions.car_addition.new')
render :new
end
end
def update
if #car_addition.update(car_addition_params)
flash[:notice] = t("car_additions.updated")
redirect_to action: :index
else
render :edit
end
end
def destroy
#car_additon.destroy
flash[:error] = t("car_additions.destroy")
redirect_to action: :index
end
private
def car_addition_params
params.require(:car_addition).permit(:name, :type, :image,
:image_cache, :remove_image)
end
end
end
I'm using devise and CanCan for authorization. Please help.
I'm pass attributes_for(:car_addition) because this is not valid attributes. When I changed this to:
attributes_for(:car_addition, name: nil) it's still not working...
You should use render_views method in order to have your views rendered in specs:
describe "PUT #update" do
render_views
# ...
end
Here's some snippets of code - if you need more, just let me know and I will post it.
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:edit, :update]
before_filter :correct_user, only: [:edit, :update]
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(params[:user]) # user_params
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
# Before filters
def signed_in_user
redirect_to signin_url, notice: "Please sign in." unless signed_in?
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
end
and then the unit test...
describe "authorization" do
...
describe "submitting a PATCH request to the Users#update action" do
before { patch user_path(wrong_user) }
specify { expect(response).to redirect_to(root_path) }
end
If I run this, I get the message:
FailureError: before { patch user_path(wrong_user) }
NoMethodError:
undefined method `patch' for # RSpec::Core::ExampleGroup::Nested_3::Nested_1::Nested_3::Nested_2::Nested_2:0x...
If I change patch to post, I get this...
FailureError: before { post user_path(wrong_user) }
ActionController::RoutingError:
No route matches [POST] "users/1497"
the error for put actually indicates that the test is getting redirected to the sign-in path, but when I test it out in the browser, it redirected to the root path as expected. I wonder if my session isn't persisting in the test. I remember let giving me some sort of trouble like this before in previous tests...
Here is the rest of the test code:
describe "for wrong user" do
let(:user) { FactoryGirl.create(:user) }
let(:wrong_user) { FactoryGirl.create(:user, email: "wrong#example.com") }
before { sign_in user, no_capybara: true }
describe "visiting Users#edit page" do
before { visit edit_user_path(wrong_user) }
it { should_not have_title(full_title('Edit user')) }
end
describe "submitting a PATCH request to the Users#update action" do
before { put user_path(wrong_user) }
specify { expect(response).to redirect_to(root_path) }
end
end # end for wrong user
patch exists in Rails 4, is you are using Rails 3.2 (that's what mentioned in your question's tags) you need to use put instead