I'm fairly new to Rails and I'm trying to get all my specs to pass. However, one of the default specs fails with the following error:
Failures:
1) RatingsController GET index assigns all ratings as #ratings
Failure/Error: expect(assigns(:ratings)).to eq([rating])
expected: [#<Rating id: 1, rottentomatoes_id: 1, rater_id: 1, rating: 10, created_at: "2014-06-17 22:58:53", updated_at: "2014-06-17 22:58:53">]
got: nil
(compared using ==)
# ./spec/controllers/ratings_controller_spec.rb:34:in `block (3 levels) in <top (required)>'
Finished in 1 minute 34.34 seconds (files took 4.55 seconds to load)
61 examples, 1 failure
Failed examples:
rspec ./spec/controllers/ratings_controller_spec.rb:30 # RatingsController GET index assigns all ratings as #ratings
Here's the spec (it's a default controller spec generated by bundle exec rails generate scaffold ...):
RSpec.describe RatingsController, :type => :controller do
include_context 'shared context'
describe "GET index" do
it "assigns all ratings as #ratings" do
rating = Rating.create! valid_attributes
get :index, {}, valid_session
expect(assigns(:ratings)).to eq([rating])
end
end
end
The shared_context:
RSpec.shared_context 'shared context' do
include Devise::TestHelpers
include Warden::Test::Helpers
Warden.test_mode!
let(:user1) { User.find_by(id: 1) || FactoryGirl.create(:user1) }
let(:user2) { User.find_by(id: 2) || FactoryGirl.create(:user2) }
let(:user3) { User.find_by(id: 3) || FactoryGirl.create(:user3) }
let(:user4) { User.find_by(id: 4) || FactoryGirl.create(:user4) }
let(:admin) { Admin.find_by(id: 1) || FactoryGirl.create(:admin) }
before do
#admin = Admin.find_by(id: 1) || FactoryGirl.create(:admin)
login_as(#admin, scope: :admin)
end
end
I should add that I'm using Devise in conjunction with the controller:
class RatingsController < ApplicationController
before_action :set_rating, only: [:show, :edit, :update, :destroy]
before_action :authenticate_admin!
# GET /ratings
# GET /ratings.json
def index
#ratings = Rating.all
end
end
I finally figured things out using this link: https://github.com/plataformatec/devise/wiki/How-To:-Controllers-tests-with-Rails-3-(and-rspec)
The document states that it is for Rails 3, but I find that it also works fine for Rails 4.
Related
I am upgrading a legacy project to rails 5 and among the rspec tests that are failing I have one that says:
Failure/Error: expect(response).to be_redirect
expected `#<ActionDispatch::TestResponse:0x00007fbe5fde51f0 #mon_owner=nil, #mon_count=0, #mon_mutex=#<Thread::...ch::Http::Headers:0x00007fbe5fdde9e0 #req=#<ActionController::TestRequest:0x00007fbe5fde5358 ...>>>>.redirect?` to return true, got false
# ./spec/controllers/search_controller_spec.rb:86:in `block (3 levels) in <top (required)>'
I am using devise gem to authenticate clients.
The tests are as follows:
describe SearchController do
before(:each) do
#client = FactoryGirl.create(:client)
end
describe "authenticated search" do
# a bunch of tests
end
describe "unauthenticated search" do
it "requires a user to be authenticated" do
get :search, params: { q: "tec" }, format: :json
expect(response).to be_redirect # FAILING HERE
end
end
end
If I run the test manually and go to /search?q=tec I get redirected to the sign_in page. The search_controller.rb has a before_action :authenticate_client!
I tried adding sign_out #client before the search but it didn't work.
Also tried current_client.reload but didn't recognize current_client.
In the authenticated search tests there is a call to stub_authenticate_client that has the following code:
def stub_authenticate_client(client)
allow(request.env['warden']).to receive(:authenticate!) { client }
allow(controller).to receive(:current_client) { client }
end
in case that is useful to solve this issue.
I also tried creating a stub_logout_client method like this:
def stub_logout_client(client)
allow(request.env['warden']).to receive(:authenticate!) { nil }
allow(controller).to receive(:current_client) { nil }
end
and calling it at the beginning of the test, but it is still passing the before_action authenticate_client!
Also tried what it was suggested here, but didn't work
The search controller that is being tested:
class SearchController < ClientApplicationController
before_action :authenticate_client!
def search
limit = params[:limit] ? params[:limit].to_i : 10
query = params[:q].to_s.downcase.strip
results = {}
if params[:report]
results[:this_report] = Report.where("SOME QUERY")
end
render json: results
end
end
Thank you!
The problem is related to the be_redirect check. Changed the test to check for content in the response and that solved it, like this:
describe "unauthenticated search" do
it "requires a user to be authenticated" do
get :search, params: { q: "tec" }, format: :json
expect(response.body).to have_content("content from the page I render")
end
end
I was writing tests for my app using responders gem.
Here are my routes:
resources :sites do
resources :pages, shallow: true
end
My PagesController chunk of code:
def create
respond_with(#page = #site.pages.create(page_params))
end
def find_site
#site = current_user.sites.find(params[:site_id])
end
And tests that are failing:
sign_in_user
let(:user_2) { create(:user) }
let(:site) { create(:site, user: #user) }
let(:page) { create(:page, site: site, user: #user) }
describe 'POST #create' do
context 'with valid attributes' do
it 'associates new page with the site' do
expect { post :create, params: { page: attributes_for(:page), site_id: site } }.to change(site.pages, :count).by(1)
end
it 'redirects to show view' do
post :create, params: { page: attributes_for(:page), site_id: site }
expect(response).to redirect_to page_path(assigns(:page))
end
end
Errors are following:
1) PagesController POST #create with valid attributes associates new page with the site
Failure/Error: expect { post :create, params: { page: attributes_for(:page), site_id: site } }.to change(site.pages, :count).by(1)
expected #count to have changed by 1, but was changed by 0
# ./spec/controllers/pages_controller_spec.rb:37:in `block (4 levels) in <top (required)>'
2) PagesController POST #create with valid attributes redirects to show view
Failure/Error: expect(response).to redirect_to page_path(assigns(:page))
ActionController::UrlGenerationError:
No route matches {:action=>"show", :controller=>"pages", :id=>nil}, missing required keys: [:id]
# ./spec/controllers/pages_controller_spec.rb:42:in `block (4 levels) in <top (required)>'
If I change site.pages in first test to Page - it's actually working.
So I am really confused how to fix this tests and where is the mistake.
Solved
Problem was with my PagesController, method create should look like this
def create
#page = #site.pages.build(page_params)
#page.user = current_user
#page.save
respond_with(#page)
end
Problem was with my PagesController, method create should look like this
def create
#page = #site.pages.build(page_params)
#page.user = current_user
#page.save
respond_with(#page)
end
Using Rails 5.1.4, Ruby 2.4.1, rspec
Scenario:
In article destroy allow only user current_ma_user with role "a,m"
Then:
Check if current_ma_user.role = "a,m"
or current_ma_user own article (#article.user)
So I create current_ma_user as hash as well as user.
Then call role to check what is user[role ]
Problems:
How to add new method to hash.
How to pass that hash.method from rspec controller_spec to controller.
Failures:
1) ArticlesController DELETE #destroy destroys the requested article
Failure/Error: delete :destroy, params: {id: article.to_param}, session: valid_session, :current_ma_user.role => "a,m"
NoMethodError:
undefined method `role' for :current_ma_user:Symbol
# ./spec/controllers/articles_controller_spec.rb:172:in `block (4 levels) in <top (required)>'
# ./spec/controllers/articles_controller_spec.rb:171:in `block (3 levels) in <top (required)>'
This is the gist
articles_controller_spec.rb:
require 'rails_helper'
RSpec.describe ArticlesController, type: :controller do
class Hash #patch to temp pass problem 1
def role
"a,m" #Hard Code, need to call user["role"] need code
end
end
user = {}
user["uid"] = "admin"
user["provider"] = "Facebook"
user["email"] = "1.0#kul.asia"
user["role"] = "a,m"
current_ma_user = user
describe "DELETE #destroy" do
it "destroys the requested article" do
article = Article.create! valid_attributes
expect {
delete :destroy, params: {id: article.to_param}, session: valid_session
}.to change(Article, :count).by(-1)
end
it "redirects to the articles list" do
article = Article.create! valid_attributes
delete :destroy, params: {id: article.to_param}, session: valid_session
expect(response).to redirect_to(articles_url)
end
end
end
Controller:
class ArticlesController < ApplicationController before_action :load_article, only: [:show, :destroy]
def destroy
if current_ma_user.role.upcase.split(',').include?("A") || current_ma_user == #article.user
#if current_ma_user == #article.user
#article.destroy
end
redirect_to :action=>'index' end
private
def load_article
#article = Article.find(params[:id]) end
end
Updated with line number:
Updated debug to show value of current_ma_user in .spec and controller
This is where your error is coming from (in your controller):
if current_ma_user.role.upcase.split(',').include?("A") || current_ma_user == #article.user
Suggested Solutions
Where is current_ma_user defined in the controller? (if it’s not assigned, then it needs to be assigned before you call the role method on the current_ma_user variable.
Try that and see how it goes.
Do something like this:
current_ma_user = User.find( params[:user_id])
Now you seem to want to pass something into the params hash. Remember to white list whatever you decide to pass into params. Whether it is user id or roles id etc, or a roles string.
When writing your tests, pass in the approrpiate values to the params hash. If you are passing in a user_id in your test, then you will have to make sure that a user is created in the test.
delete :destroy, {:id => article.id.to_s, :user_id => #current_ma_user.id }, session: valid_session
also perhaps in your spec file, in your test, put the current_ma_user in a before filter and make it an instance variable so it will be accessible to all your tests:
before(:each) do
#current_ma_user = user.create( <--- create the user with the
appropriate attributes here --->)
end
Warning: Untested
I just typed it into the stack overflow editor.
I have this rspec code:
let(:valid_attributes) {
{name: "Sample Product"}
}
describe "#index" do
it "should give a collection of products" do
product = Product.create! valid_attributes
get :index, :format => :json
expect(response.status).to eq(200)
expect(response).to render_template("api/products/index")
expect(assigns(:products)).to eq([product])
end
end
And it controller:
def index
#products = Product.all
end
But the controller code still doesn't satisfy the spec. What is wrong here.
Here is the failure message:
Failures:
1) Api::ProductsController#index should give a collection of
products
Failure/Error: expect(assigns(:products)).to eq([product])
expected: [#<Product id: 3, name: "Sample Product", created_at: "2016-06-15 05:10:50", updated_at: "2016-06-15 05:10:50">]
got: nil
(compared using ==)
# ./spec/controllers/api/products_controller_spec.rb:53:in `block (3 levels) in <top (required)>'
Finished in 0.05106 seconds (files took 1.89 seconds to load) 1
example, 1 failure
You have:
get :index, :format => :json
I assume you should have:
get :index, :format => :html
By default, rails returns html, and you didn't specify otherwise in your index action.
assign checks that an instance variable was set
and products should be created in database (use let!(with bang) for it), then:
in your controller:
def index
#products = Product.all # `#products` should be present
end
in rspec:
let(:valid_attributes) {{ name: 'Sample Product' }}
let!(:product) { Product.create!(valid_attributes) } # use `!` here
describe "#index" do
before { get :index, format :json }
it 'should give a collection of products' do
expect(assigns(:products)).to eq([product])
end
end
i have used devise in rspec testing. this is my test
describe BooksController do
before(:all) do
#user = FactoryGirl.create(:user)
end
describe "GET index" do
it "shows list of current user books" do
sign_in #user
book = #user.books.create!(:title => "user")
get :index, {}
assigns(:books).should eq(#user.books)
end
end
describe "GET show" do
it "assigns the requested book as #book" do
sign_in #user
book = #user.books.create!(:title => "user")
visit_count = book.visits.to_i
get :show, {:id => book.to_param}
assigns(:book).should eq(book)
book = Book.find(book.id)
visit_count.should_not eq(book.visits)
end
end
describe "GET new" do
it "assigns a new book as #book" do
sign_in #user
get :new, {}
assigns(:book).should be_a_new(Book)
end
end
end
factory
FactoryGirl.define do
factory :user do
sequence(:email) { |n| "foo#{n}#example.com" }
password '12345678'
password_confirmation '12345678'
confirmed_at Time.now
end
end
book controller
class BooksController < ApplicationController
before_action :authenticate_user!, only: [:index, :edit, :update, :destroy, :new, :my_books, :add_wish_list]
# GET /books
# GET /books.json
def index
#books = current_user.books
end
# GET /books/1
# GET /books/1.json
def show
#book = Book.find(params[:id])
#book.book_visit_count
if(session["warden.user.user.key"].present?)
#book.book_visit_user(session["warden.user.user.key"][0][0])
end
end
# GET /books/new
def new
#book = Book.new
end
end
error
Failure/Error: assigns(:book).should be_a_new(Book)
expected nil to be a new Book(id: integer, title: string, author: string, isbn_10: string, isbn_13: string, edition: integer, print: integer, publication_year: integer, publication_month: string, condition: string, value: integer, status: boolean, stage: integer, description: text, visits: integer, user_id: integer, prefered_place: string, prefered_time: string, created_at: datetime, updated_at: datetime, rating: integer, image: string, publisher: string, goodreads_id: string)
# ./spec/controllers/books_controller_spec.rb:66:in `block (3 levels) in <top (required)>'
the problem is the third test "get new" fails when i run the test as a whole but passes when i run it individually. and also if i remove the before_authenticate! in controller then all test passes.
Again if i commented out the "assigns" in first two describe blocks then all tests pass again.
i am using rails 4.0.2 and rspec 2.14.7 , devise 3.2.2
The only thing I can figure is that your authenticate_user method is failing for users that have previously been authenticated. It's not affecting show because you don't have :show listed in your before_action. You could test this theory by requiring authentication for show as well and seeing if your second example starts failing for before(:all) as well.