i'm using rails 4.1.1 and rspec 3.0.3.
here is the rspec code.
require 'rails_helper'
describe MembersController, type: :controller do
describe 'GET index' do
it 'should be success' do
puts "11111"
get :index
puts "33333"
expect(response).to render_template :index
end
end
end
and :index action of controller is,
def index
puts "22222"
#members = Member.order('mem_id DESC').paginate(page: params[:page]||1, per_page: params[:per_page]||30)
end
==============================
and i did 'rake spec', i exepcted the console to print like this.
11111
22222
33333
but the result is,
11111
33333
and it makes an error message like this.
Failure/Error: expect(response).to render_template :index
expecting <"index"> but rendering with <[]>
what's wrong with my rspec?
----------------------------------- UPDATE QUESTION ------------------------------
this is the spec/rails_helper.rb (all comments are removed)
ENV["RAILS_ENV"] ||= 'test'
require 'spec_helper'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
config.infer_spec_type_from_file_location!
end
Related
Assumptions
We are developing with Ruby on Rails and creating test code with Rspec.
Environment
Ruby 2.6.5
Rails 6.0.3
RSpec 3.11
devise 4.7.1
What I would like to achieve.
In Rspec, I am expecting a 200 code, but it returns a 401 code.
I modified the code below to log in, but it still fails to log in and returns a 401 code.
How can I modify it so that it logs in and returns a 200 code?
Problem/error message you are encountering
Rspec logs
Failure/Error: expect(response.status).to eq 200
expected: 200
got: 401
Source code in question
infos_controller_spec.rb
# frozen_string_literal: true
require "rails_helper"
module Api::V1::Admin
RSpec.describe InfosController, type: :controller do
describe "GET /form_init" do
let!(:user){create :user}
it "should 200" do
login_user(user)
get :form_init
expect(response.status).to eq 200
end
end
end
end
spec/rails_helper.rb
require 'spec_helper'
require "devise"
Dir[File.expand_path("./spec/support/*.rb")].each{|f| require f}
RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = :expect
end
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::IntegrationHelpers, type: :request
config.include ControllerMacros, type: :controller
config.include Warden::Test::Helpers
end
spec/support/controller_macros.rb
module ControllerMacros
def login_user(user)
#request.env['devise.mapping'] = Devise.mappings[:user]
sign_in(user, scope: :user)
end
end
I am getting this error when using RSpec to test an index action and response in a Rails controller:
JSON::ParserError:
A JSON text must at least contain two octets!
The most common fix -- including render_views -- is not working and nil is not being passed in. The test is not hitting the view. When I insert render json: {test: 'hello world'}, status: 200 and return in the index action of the controller, and a pry in the view (index.json.jbuilder) and after the get :index in the test, I can see there is a response body. If I amend the test expectation to expect(response).to render_template '[]' I can see the empty array that should be in the response body. Why is render_views failing and how to I get it working again?
Here is the index_spec.rb:
require 'rails_helper'
RSpec.describe ThingsController, type: :controller do
render_views
let(:json_response) { JSON.parse(response.body) }
let(:status) { response.status }
let(:user) { create(:user_with_associations) }
subject{ ThingsController }
describe "GET #index" do
context "(success cases)" do
before(:each) do
expect_any_instance_of(subject).to receive(:set_user_by_token).and_return(user)
end
context "and when there are no things" do
before(:each) do
get :index
end
it "returns a 200 status" do
expect(status).to eq 200
end
it "returns a top level key of data with an empty array" do
expect(json_response["data"]).to eq []
end
end
end
end
Here is the rails_helper.rb:
ENV["RAILS_ENV"] ||= 'test'
require_relative 'spec_helper'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.infer_spec_type_from_file_location!
end
And here is the spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require 'factory_girl_rails'
require 'faker'
include ActionDispatch::TestProcess
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.include FactoryGirl::Syntax::Methods
config.before do
FactoryGirl.factories.clear
FactoryGirl.find_definitions
end
end
And here's the controller action under test things_controller.rb:
class ThingsController < ApplicationController
before_action :authenticate_user!, only: [ :index ]
before_action -> {check_last_updated("Thing")}, only: [ :index ]
def index
#things = #current_user.things.in_last_three_months.approved_and_unapproved.order(start_time: :asc)
end
end
Rails 4.2.0
Ruby 2.1.2
RSpec 3.5.4
This is my first question here, so let me know if there is other information that should be included.
You're not correctly stubbing out the authentication in your spec. You say
expect_any_instance_of(subject).to receive(:set_user_by_token).and_return(user)
but you should say
allow_any_instance_of(subject).to receive(:set_user_by_token).and_return(user)
As a general rule, *_any_instance_of methods should be avoided if possible because they can be ambiguous in more nuanced situations. In controller specs, you can use controller to access the instance of the controller being tested. e.g.
allow(controller).to receive(:set_user_by_token).and_return(user)
Im writing some tests for my controller, but im getting the error below when i'm running rspec ./spec/controllers, when i just run the spec direct in the file i'm getting everything green.
1) VersionOne::UsersController GET #actives should return all actives
Failure/Error: get :actives
ActionController::UrlGenerationError:
No route matches {:action=>"actives", :controller=>"version_one/users"}
# ./spec/controllers/version_one/users_controller_spec.rb:13:in `block (3 levels) in <top (required)>'
2) VersionOne::UsersController GET #archived should return all archives
Failure/Error: get :archives
ActionController::UrlGenerationError:
No route matches {:action=>"archives", :controller=>"version_one/users"}
# ./spec/controllers/version_one/users_controller_spec.rb:23:in `block (3 levels) in <top (required)>'
3) VersionOne::UsersController POST #create should create a new collaborator with success
Failure/Error: post :create, params: params
This is my controller
class UsersController < ApiControler
def actives
users = User.actives
render json: users
end
def archives
users = User.archiveds
render json: users
end
def create
user = User.build(user_params)
render json: user
end
end
This are my routes
scope 'api' do
scope 'v1', module: 'version_one' do
resources 'users' do
collection do
get 'actives'
get 'archives'
end
member do
match 'active'
match 'archive'
end
end
end
end
This are my tests
RSpec.describe VersionOne::UsersController, type: :controller do
before(:all) do
7.times { Collaborator.build(attributes_for(:user)) }
8.times { Admin.build(attributes_for(:user)) }
5.times { Collaborator.build(attributes_for(:user)).archived! }
end
describe "GET #actives" do
it "should return all actives" do
get :actives
body = JSON.parse(response.body)
expect(response).to have_http_status(:success)
expect(body["pagination"]["per_page"]).to eq(20)
expect(body["pagination"]["total_objects"]).to eq(15)
end
end
describe "GET #archived" do
it "should return all archives" do
get :archives
body = JSON.parse(response.body)
expect(response).to have_http_status(:success)
expect(body["pagination"]["per_page"]).to eq(20)
expect(body["pagination"]["total_objects"]).to eq(5)
end
end
describe "POST #create" do
it "should create a new collaborator with success" do
params = { kind: "Collaborator", user: { name: "User", email: "user#email.net", password: "123456", cpf: "36156291830" } }
post :create, params: params
body = JSON.parse(response.body)
expect(response).to have_http_status(:success)
expect(body).to include(
"name" => "User",
"cpf" => "36156291830",
"email" => "user#email.net",
)
end
end
end
this is my .rspec
--color
--format documentation
--require rails_helper
this is my rails_helper
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
require 'shoulda-matchers'
require 'rack/test'
require 'faker'
require 'rake'
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.include Shoulda::Matchers::ActiveModel, type: :model
config.include Shoulda::Matchers::ActiveRecord, type: :model
config.fixture_path = "#{::Rails.root}/spec/factories"
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation)
end
config.use_transactional_fixtures = false
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
am'i forgetting something?
You do not have namespaces for UsersController
try:
module Api
module VersionOne
class UsersController < ApiController
# your code
end
end
end
You may also need to change ApiController to Api::VersionOne::ApiController
saviors.
I'm having a trouble with cleaning database after each RSpec example.
The thing is, that when I run rspec command, users_controller_spec.rb complains that there are more records than the example expects. Indeed the records are being created as it says if I check with rails c.
when I run the this suite alone, it will be successful, so I assume it is because DatabaseCleaner doesn't clean the user records which other specs create(the number of user records matches the extra records users_controller_spec example claims to be). They are created in before :all block(if that matters).
Here is my rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require 'spec_helper'
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
require 'devise'
require 'admin/v1/dashboard_controller'
# Requires supporting ruby files with custom matchers and macros, etc, in
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.include Devise::Test::ControllerHelpers, type: :controller
config.include ControllerMacros, type: :controller
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
config.include FactoryGirl::Syntax::Methods
config.infer_spec_type_from_file_location!
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run
end
end
end
users_controller.rb
describe 'GET #index' do
it 'populates an array of users' do
user1 = create(:user)
user2 = create(:user)
get :index
expect(assigns(:users)).to match_array([user1, user2])
end
it 'renders :index template' do
get :index, {}
expect(response).to render_template :index
end
end
UPDATE1: this is where the extra user records are created
require 'rails_helper'
describe Admin::V1::MessagesController do
let(:admin_user) do
admin_user = double('admin_user')
allow(request.env['warden']).to receive(:authenticate!).and_return(admin_user)
allow(controller).to receive(:current_admin_v1_admin_user).and_return(admin_user)
p '==='
end
before { login_admin_user admin_user }
describe 'GET #index' do
it 'renders :index template' do
get :index, {}
expect(response).to render_template :index
end
end
describe 'GET #get_users' do
before :all do
#user1 = create(:user, nickname: 'hiro')
#user2 = create(:user, nickname: 'elise')
end
context 'with params' do
it 'populates an array of users matching on nickname' do
get :get_users, format: :json, query: 'h'
expect(assigns(:users)).to match_array([#user1])
end
end
context 'without params' do
it 'populates an array of all users' do
get :get_users, format: :json
expect(assigns(:users)).to match_array([#user1, #user2])
end
end
end
describe 'GET #get_messages' do
before :all do
#user1 = create(:user)
#user2 = create(:user)
#message1 = create(:message, user_id: #user1.id)
#message2 = create(:message, user_id: #user1.id)
#message3 = create(:message, user_id: #user2.id)
end
context 'with user_id' do
it 'populates an array of messages with the user_id' do
get :get_messages, format: :json, user_id: #user1.id
expect(assigns(:messages)).to match_array([#message1, #message2])
end
end
end
end
Unfortunately RSpec's before(:all) does not play nicely with transactional tests. The code in before(:all) gets run before the transaction is opened, meaning any records created there will not be rolled back when the transaction is aborted. You are responsible for manually cleaning these items up in an after(:all).
See rspec-rails#496 and Using before(:all) in RSpec will cause you lots of trouble unless you know what you are doing
after(:all) do
# before/after(:all) is not transactional; see https://www.relishapp.com/rspec/rspec-rails/docs/transactions
DatabaseCleaner.clean_with(:truncation)
end
I have this route:
post 'create', to: 'applications#create'
this controller:
class ApplicationsController < ApplicationController
def create
end
end
and this test:
require "spec_helper"
describe ApplicationsController do
describe "routing" do
it 'routes to #create' do
post('/applications').should route_to('applications#create')
end
end
end
and this is my spec_helper:
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = :should
end
config.mock_with :rspec do |c|
c.syntax = :should
end
config.use_transactional_fixtures = true
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
end
Why when I run
rspec ./spec/routing
I get this error?
Failure/Error: post('/applications').should route_to('applications#create')
NoMethodError:
undefined method `post' for RSpec
I tried changing the test using expect but with no luck. neither with a different class name.
What should I do to test my routes?
UPDATE
If I do this:
it "should return status 200" do
post('/applications'), {}
response.status.should be(200)
end
I get the same error
I am not sure it can help you but I had the same problem when passing to Rspec 3
According to https://relishapp.com/rspec/rspec-rails/docs/directory-structure
we should enable that option to pass automatically metadata.
# spec/rails_helper.rb
RSpec.configure do |config|
config.infer_spec_type_from_file_location!
end
Once this was added my specs passed.