Having probleman with ActionController::UrlGenerationError on RSPEC - ruby-on-rails

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

Related

Problems for request rspec with basic http auth using Devise

Devise: 4.20
Rails: 5.0.1
Rspec: 3.5
I had used this link https://github.com/plataformatec/devise/wiki/How-To:-Use-HTTP-Basic-Authentication, but I havproblems to test the http basic auth for my api using rspec for requests tests. Below is the example the error:
app/controllers/api/base_controller.rb
module Api
class BaseController < ApplicationController
before_action :authenticate_user!
protected
def authenticate_user!
authenticate_or_request_with_http_basic do |username, password|
resource = User.find_by_username(username)
if resource
sign_in :user, resource if resource.valid_password?(password)
else
request_http_basic_authentication
end
end
end
end
end
app/controllers/api/v1/car_controller.rb
module Api
module V1
class CarController < Api::BaseController
respond_to :json
def index
#cars = Car.all
render :json => {:content => #cars}, :status => 200
end
end
end
end
spec/requests/api/v1/car_controller_spec.rb
require 'rails_helper'
RSpec.describe "Servers API", :type => :request do
it 'sends a list of servers' do
admin = FactoryGirl.create(:admin)
#env = {}
#env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(admin.username, admin.password)
get "/api/cars", :params => {}, :headers => #env
# test for the 200 status-code
expect(response.status).to eq(200)
end
end
When I run the spec, I have the below error:
# --- Caused by: ---
# NoMethodError:
# undefined method `sign_in' for #<Api::V1::CarController:0x0000000609ef12>
# ./app/controllers/api/base_controller.rb:10 in block in authenticate_user!
Anyone can help me? Thanks.
I have similar setup where my specs are passing, would you also show your spec_helper content, looks like you are not including Devise::TestHelpers.
spec_helper
RSpec.configure do |config|
config.include Devise::TestHelpers
config.include Warden::Test::Helpers
config.before { Warden.test_mode! }
config.after { Warden.test_reset! }
config.before(:each) do
#headers = { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
end
end
and my test looks something like this:
RSpec.describe 'Users' do
context 'when client is authorized' do
let(:user) { FactoryGirl.create(:user) }
it 'gets user' do
#headers['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Token.
encode_credentials(
user.authentication_token,
email: user.email
)
get api_v1_user_url(id: user.id), {}, #headers
expect(response.status).to eq(200)
end
end
end

DatabaseCleaner doesn't seem to clean between suits

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

SImplecov - Incorrect test coverage for Grape API

I have a Rails 4.2 app with an API based on Grape. I started to write tests for it using Rpsec. My tests work great and test what I expected. But when I run rspec at terminal, Simplecov is not showing the correct coverage for the api files, as you can see on the image bellow.
The files on the dir /lib/api/app do have some coverage. But Simplecov shows them as 0% covered.
To compare, I ran specs inside RubyMine with the built in coverage tool, and it shows the correct coverage:
So, am I missing something here? whats wrong with simplecov?
this is my rails_helper.rb :
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 'simplecov'
SimpleCov.start 'rails'
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.include RSpec::Rails::RequestExampleGroup, type: :request, file_path: /spec\/api\/v1/
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
config.infer_spec_type_from_file_location!
Faker::Config.locale = 'pt-BR'
end
This is one of API endpoints, trips.rb:
module Api
module App
class Trips < Grape::API
include Grape::Kaminari
resource :trips do
desc 'Return a list of trips of a vehicle.'
params do
requires :vehicle_id, type: Integer, desc: 'id of the vehicle'
optional :page, type: Integer, desc: 'Page of registers. Default to 1 (first page).'
optional :per, type: Integer, desc: 'Number of registers per page. Default to 25.'
end
get do
vehicle = Vehicle.find params[:vehicle_id]
if vehicle.user == current_user
trips = Trip.where(vehicle_id: vehicle.id ).order(started_at: :desc)
present paginate(trips), with: Entities::Trip
else
error!('Unauthorized', 401)
end
end
desc 'Return a Trip'
params do
requires :id, type: Integer, desc: 'id of the Trip'
end
route_param :id do
get do
trip = Trip.find params[:id]
if trip.vehicle.user == current_user
present trip, with: Entities::Trip
else
error!('Unauthorized', 401)
end
end
end
end
end
end
end
And this is a example spec that should be 100% covered (trips_spec.rb):
describe Api::App::Trips do
include ApiHelpers
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:vehicle) { create(:vehicle, user_id: user.id) }
let(:vehicle2) { create(:vehicle, user: user2) }
let(:trip) { create(:trip, vehicle: vehicle) }
let(:trip2) { create(:trip, vehicle: vehicle2) }
let(:auth_headers) { user.create_new_auth_token }
describe 'GET /api/v1/trips/:id' do
context 'when not authenticated' do
it 'returns 401 unauthorized' do
get "/api/v1/trips/#{trip.id}"
expect(response).to have_http_status(401)
end
end
context 'when user owns the vehicle' do
it 'returns a trip by id' do
get "/api/v1/trips/#{trip.id}", nil, auth_headers
expect(response.status).to eq(200)
expect(json_response).to be_an Hash
end
end
context 'when vehicle is from another user' do
it 'returns error 404' do
get "/api/v1/trips/#{trip2.id}", nil, auth_headers
expect(response.status).to eq(401)
end
end
end
describe 'GET /api/v1/trips' do
context 'when user owns the vehicle' do
it 'returns a list of trips by vehicle id' do
get "/api/v1/trips?vehicle_id=#{vehicle.id}", nil, auth_headers
expect(response.status).to eq(200)
expect(json_response).to be_an Array
end
end
context 'when vehicle belongs to another user' do
it 'returns error 404' do
get "/api/v1/trips?vehicle_id=#{vehicle2.id}", nil, auth_headers
expect(response.status).to eq(401)
end
end
end
end
So, I figured out the problem: I was invoking Simplecov on rails_helper.rb. The correct place to invoke it is on spec_helper.rb, at the very beginning .

rails 4 mocking session in rspec

gemfile:
source 'https://rubygems.org'
gem 'rails', '4.1.1'
gem 'mysql2
spec/spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment",__FILE__)
require 'rspec/rails'
require "capybara/rspec"
include Capybara::DSL
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
spec/support/controller_macros.rb
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
# user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the "confirmable" module
sign_in user
end
end
def load_key
session[:master_key] = SecureRandom.hex(24)
end
end
spec/factories/factory.rb
FactoryGirl.define do
factory :user do
email "test#test.com"
password "12345678"
end
end
categories_controller.rb
class CategoriesController < ApplicationController
before_filter :authenticate_user!, :load_key!
def index
#categories = Category.where("user_id is null or user_id = ?", current_user).order(updated_at: :desc)
end
private
def category_params
params.require(:category).permit(:title)
end
end
spec/controllers/categories_controller_spec.rb
require 'rails_helper'
describe CategoriesController do
login_user
load_key
it "get list of categories" do
get :index
expect(response).to render_template("index")
end
end
application_controller.rb
class ApplicationController < ActionController::Base
def load_key!
redirect_to(key_path) unless session[:master_key]
end
end
I would like to bypass a load_key! before_filer in CategoriesController. I added method load_key to controller_macros.rb. But it gives me the error:
undefined local variable or method 'session' for RSpec::ExampleGroups::CategoriesController:Class (NameError) from d:/sites/key_manager/spec/support/controller_macros.rb:19:in `load_key'
Looks like session variable is unavailable under rspec.
The answer was a quite simple.
I should use request.session rather than session.
In this case I should transfer a session variable assign from controller_macros.rb to categories_controller_spec.rb:
categories_controller_spec.rb
require 'rails_helper'
require 'spec_helper'
describe CategoriesController, :type => :controller do
login_user
it "redirect to key form when key was't loaded" do
get :index
expect(response).to redirect_to(key_path)
end
it "view password list #index" do
request.session[:master_key] = SecureRandom.hex(24) # <======================
get :index
expect(response).to render_template(:index)
end
end
controller_macros.rb
module ControllerMacros
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:admin) # Using factory girl as an example
end
end
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
# user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the "confirmable" module
sign_in user
end
end
end

ControllerMacros cannot be seen in RSpec

I have a Rails app and I am trying to test it. I use devise to log in. However, I faced a problem that occurs when I want to test:
Users/ender/Projects/ratingw/spec/controllers/widgets_controller_spec.rb:4:in `block in <top (required)>': undefined local variable or method `login_user' for #<Class:0x007fe909bd5070> (NameError)
First, I want to say that I read this devise formal tutorial.
My spec_helper.rb is:
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:twitter] = {
:provider => 'twitter',
:uid => '123545'
# etc.
}
OmniAuth.config.mock_auth[:twitter] = :invalid_credentials
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
config.include RequestMacros, :type => :request
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# 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
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
end
module ::RSpec::Core
class ExampleGroup
include Capybara::DSL
include Capybara::RSpecMatchers
end
end
and also I have a controller_macros.rb, which is located in support file:
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = Factory(:user)
#user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
sign_in user
end
end
end
and finally, my controller_spec file is:
require 'spec_helper'
describe WidgetsController do
login_user
describe "User" do
before(:each) do
#current_user = mock_model(User, :id => 1)
#widget = mock_model(Widget, :user_id => 1)
Widget.stub!(:current_user).and_return(#current_user)
Widget.stub!(:widgets).and_return(#widget)
end
it "should have a current_user" do
subject.current_user.should_not be_nil
redirect_to widgets_path
end
it "should not have a current_user" do
redirect_to widgets_path new_user_session_path
end
end
def mock_widget(stubs={})
#mock_widget ||= mock_model(Widget, stubs).as_null_object
end
describe "Get index" do
it "should get all widgets " do
Widget.stub(:all) { [mock_widget] }
get :index
assigns(:widgets) == #widgets
end
end
describe "Post widget" do
it "creates a new widget" do
Widget.stub(:new).with({'these' => 'params'}) { mock_widget(:save => true) }
post :create, :widget => {'these' => 'params'}
assigns(:widget) == #widgets
response.should redirect_to (edit_widget_path(#mock_widget))
end
it "can not create a new widget" do
Widget.stub(:new).with({'these' => 'params'}) { mock_widget(:save => false) }
post :create, :widget => {'these' => 'params'}
assigns(:widget) == #widgets
redirect_to new_widget_path
end
end
describe "Get widget" do
it "shows exact widget via its uuid" do
Widget.stub(:find).with("10") { mock_widget(:save => true) }
get :show
assigns(:widget) == #widget
end
end
describe "Put widget" do
it "updates the widget attributes" do
Widget.stub(:find_by_uuid).with("6").and_return(mock_widget(:update_attributes => true))
mock_widget.should_receive(:update_attributes).with({'these' => 'params'})
put :update, :uuid => "6", :widget => {'these' => 'params'}
response.should redirect_to (edit_widget_path(#mock_widget))
end
it "can not update the widget attributes" do
Widget.stub(:find_by_uuid).with("6").and_return(mock_widget(:update_attributes => false))
mock_widget.should_receive(:update_attributes).with({'these' => 'params'})
put :update, :uuid => "6", :widget => {'these' => 'params'}
end
end
describe "delete destroy" do
it "destroys the requested widget" do
Widget.stub(:find_by_uuid).with("10").and_return(mock_widget)
mock_widget.should_receive(:destroy)
get :destroy, :uuid => "10"
end
end
end
How can I fix this? What is the problem?
I guess the error stems from:
config.extend ControllerMacros, :type => :controller
You should have:
config.include ControllerMacros, :type => :controller

Resources