I'm trying to test my api with airborne
The call to the api works and create a User, but in the dev DB : there is no log of the creation and i find it using rails c.
But when i do User.find in the test, it searches the test db : there are logs and it doesn't find the user.
Here's my test :
require 'rails_helper'
describe 'register#create' do
it 'should create a new user' do
post '/register',
{
email: 'mail#mail.fr',
password: '12345678',
password_confirmation: '12345678'
}
puts response.body
expect_status 200
expect(User.find_by_email('mail#mail.fr')).to exist
end
end
Here's the method i'm trying to test :
class RegistrationsController < Devise::RegistrationsController
respond_to :json
skip_before_filter :verify_authenticity_token
acts_as_token_authentication_handler_for User
skip_before_filter :authenticate_entity_from_token!, only: [:create]
skip_before_filter :authenticate_entity!, only: [:create]
skip_before_filter :authenticate_scope!
append_before_filter :authenticate_scope!, only: [:destroy]
def create
build_resource(sign_up_params)
if !resource.valid?
status = 422
message = "#{resource.errors.full_messages}"
elsif resource.save!
status = 200
message = "Successfully created new account for email #{sign_up_params[:email]}."
else
clean_up_passwords resource
status = 500
message = "Failed to create new account for email #{sign_up_params[:email]}."
end
respond_to do |format|
format.json { render json: { message: message }, status: status }
end
end
Here's 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'
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
end
Here's spec_helper :
ENV['RAILS_ENV'] = 'test'
require 'airborne'
Airborne.configure do |config|
config.base_url = 'http://myapp.dev/api/v1'
end
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
end
And finally, here's the log :
$ rspec
WARN: Unresolved specs during Gem::Specification.reset:
minitest (~> 5.1)
WARN: Clearing out unresolved specs.
Please report a bug if this causes problems.
DEBUG -- : ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
DEBUG -- : (0.1ms) begin transaction
{"message":"Successfully created new account for email mail#mail.fr."}
DEBUG -- : User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT 1 [["email", "mail#mail.fr"]]
DEBUG -- : (0.1ms) rollback transaction
F
Failures:
1) register#create should create a new user
Failure/Error: expect(User.find_by_email('mail#mail.fr')).to exist
expected nil to exist but it does not respond to either `exist?` or `exists?`
# ./spec/registration_controller_spec.rb:19:in `block (2 levels) in <top (required)>'
I'm fairly new to rails and i don't know where the problem might come from.
Thanks for your help :)
In my test, i POST to http://myapp.dv/api/v1/register which is a Pow! url.
Pow! configuration was the default one, which points to the DEV env.
So my test was in the right env, not the call to the api.
I used powder to switch envs now and it works.
ps : i also replaced
expect(User.find_by_email('mail#mail.fr')).to exist
with
expect(User.find_by_email('mail#mail.fr')).not_to be_nil
Related
I am getting the same behavior whether it's in the browser or running rspec... How can this issue get resolved?
I have tried removing the before_filter :authenticate_user! within application_controller.rb and the result remains the same. The failing example is the very first one within spec/features/subdomain_feature_spec.rb
Failures:
1) subdomains redirects invalid accounts
Failure/Error: visit root_url(subdomain: 'random-subdomain')
Capybara::InfiniteRedirectError:
redirected more than 5 times, check for infinite redirects.
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/rack_test/browser.rb:41:in `process_and_follow_redirects'
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/rack_test/browser.rb:22:in `visit'
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/rack_test/driver.rb:43:in `visit'
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/session.rb:254:in `visit'
# /home/benny/.rvm/gems/ruby-2.3.3/gems/capybara-2.13.0/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/subdomain_feature_spec.rb:7:in `block (2 levels) in <top (required)>'
...
Failed examples:
rspec ./spec/features/subdomain_feature_spec.rb:6 # subdomains redirects invalid accounts
Relevant Files
spec/features/subdomain_feature_spec.rb
require 'rails_helper'
describe 'subdomains' do
let!(:account) { create(:account_with_schema) }
it 'redirects invalid accounts' do
visit root_url(subdomain: 'random-subdomain')
expect(page.current_url).to_not include('random-subdomain')
end
it 'allows valid accounts' do
visit root_url(subdomain: account.subdomain)
expect(page.current_url).to include(account.subdomain)
end
it 'forces user to login before accessing subdomain content' do
visit root_url(subdomain: account.subdomain)
expect(page).to have_content 'sign in or sign up before continuing'
end
end
config/routes.rb
class SubdomainPresent
def self.matches?(request)
request.subdomain.present?
end
end
class SubdomainBlank
def self.matches?(request)
request.subdomain.blank?
end
end
Rails.application.routes.draw do
constraints(SubdomainPresent) do
root 'projects#index', as: :subdomain_root
devise_for :users
end
# only allow certain routes when there isn't a subdomain
constraints(SubdomainBlank) do
root 'welcome#index'
resources :accounts, only: [:new, :create]
end
end
controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_filter :load_schema, :authenticate_user!
private
def load_schema
Apartment::Tenant.switch!('public')
return unless request.subdomain.present?
account = Account.find_by(subdomain: request.subdomain)
if account
Apartment::Tenant.switch!(request.subdomain)
else
redirect_to root_url(subdomain: :false)
end
end
def after_sign_out_path_for(resource_or_scope)
new_user_session_path
end
end
controllers/welcome_controller.rb
class WelcomeController < ApplicationController
skip_before_filter :authenticate_user!, only: :index
def index
end
end
spec/factories/accounts.rb
FactoryGirl.define do
sequence(:subdomain) { |n| "subdomain#{n}" }
factory :account do
sequence(:subdomain) { |n| "subdomain#{n}" }
association :owner, factory: :user
factory :account_with_schema do
after(:build) do |account|
Apartment::Tenant.create(account.subdomain)
Apartment::Tenant.switch!(account.subdomain)
end
after(:create) do |account|
Apartment::Tenant.reset
end
end
end
end
spec/rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
# Add additional requires below this line. Rails is not loaded until this point!
require 'spec_helper'
require 'rspec/rails'
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
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.use_transactional_fixtures = false
config.infer_spec_type_from_file_location!
config.include FactoryGirl::Syntax::Methods
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end
# Capybara.register_driver :poltergeist do |app|
# Capybara::Poltergeist::Driver.new(app, options)
# end
The Capybara rack_test driver ignores hostnames so you're not going to be able to test subdomain behavior with it, you'll need to run those tests with one of the drivers that utilizes/mimics a full browser (selenium, poltergeist, capybara-webkit).
Additionally you want to write and url/path check using the Capybara have_current_path matcher rather than eq with `current_url'
expect(page).not_to have_current_path(/random-subdomain/, url: true)
posts_controller.rb
class PostsController < ApplicationController
respond_to :html, :xml, :json
before_filter :authenticate_user!, :except => [:show, :index]
before_filter :admin_only, :except => [:show, :index]
def new
#post = Post.new
end
end
spec_helper.rb
# 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 'forgery'
require 'populators'
# 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|
config.mock_with :rspec
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/test/fixtures"
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.after(:suite) do
DatabaseCleaner.clean
end
# 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 = false
end
posts_controller_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'
describe PostsController do
fixtures :all
include Devise::TestHelpers
render_views
before(:each) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.start
end
after(:each) do
DatabaseCleaner.clean
end
it "new action should render new template" do
get :new
response.should render_template(:new)
end
end
ruby 1.8.7, rspec 2.11, Rails 3.2.19
I get this error expecting <"new"> but rendering with <""> how can I pass this case ... I tried many suggestions but didnt get any success .. Please Help Am stuck with it ... :(
NOTE: I cant change the code of a controller
# Use the sign_in helper to sign in a fixture `User` record.
user = users(:one)
#request.env['warden'].stub(:authenticate!).and_return(user)
#controller.stub(:current_user).and_return(user)
get :new
assert_template 'new'
this works for me using https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs and thanx #Зелёный for the direction :)
Original question
I'm trying to write some feature tests with RSpec and Capybara and wonder, why my gon object is always empty allthough it was set in my controller:
app/controllers/timetrackings_controller.rb:
class TimetrackingsController < ApplicationController
include ApplicationHelper
before_action :authenticate_user!
before_action :set_timetracking, only: [:update, :destroy]
# GET /timetrackings
def index
projects = Project.all_cached.select('id, name, category, number, customer_id').includes(:customer).where(:archived => false).order(:number)
gon.projects = group_by_customer(projects).to_h
gon.services = Service.all_cached.select('id, name').where(:archived => false).order('LOWER(name)')
end
...
Now I've wondered why this data doesn't get rendered into my view (debugged with save_and_open_page: //<![CDATA[ window.gon={};gon.projects={};gon.services=[]; //]]>), so I just tried to get the gon values in my test file:
require 'spec_helper'
describe 'the timetracking page', :js => true do
before :each do
switch_to_subdomain('test')
#project = FactoryGirl.create(:project)
#service = FactoryGirl.create(:service)
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
visit '/timetracking'
save_and_open_page
end
it 'renders the react component' do
expect(page).to have_selector '#timetracking-form'
end
it 'allows me to create a new timetracking' do
within('#timetracking-form') do
fill_in 'duration', :with => '2'
puts '########'
puts Gon.all_variables
puts Project.all.to_json
puts Service.all.to_json
puts '########'
find_field('project_id').find("option[value='#{#project.id}']").click
end
click_button '.ei-icon-check'
end
end
But the output is just:
########
{}
[{"id":2,"number":"2","name":"project2","description":"Some notes","archived":false,"customer_id":2,"created_at":"2015-11-30T16:05:40.160+01:00","updated_at":"2015-11-30T16:05:40.160+01:00","rate_type":null,"hourly_rate":null,"service_rates":null,"budget_type":null,"budget_rate":null,"category":"A","deadline":null}]
[{"id":2,"name":"service2","description":"Some notes","archived":false,"created_at":"2015-11-30T16:05:40.173+01:00","updated_at":"2015-11-30T16:05:40.173+01:00","billable":null,"hourly_rate":null}]
########
Thats my spec_helper.rb:
require 'devise'
# Setup Capybara
require 'capybara/rspec'
require 'capybara/poltergeist'
Capybara.always_include_port = true
Capybara.javascript_driver = :poltergeist
# Use SimpleCov for code coverage
require 'simplecov'
require 'simplecov-shield'
SimpleCov.start 'rails'
SimpleCov.formatters = [
SimpleCov::Formatter::HTMLFormatter,
SimpleCov::Formatter::ShieldFormatter
]
# 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 'shoulda-matchers'
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
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.check_pending! if defined?(ActiveRecord::Migration)
module ControllerMacros
def attributes_with_foreign_keys(*args)
FactoryGirl.build(*args).attributes.delete_if do |k, v|
['id', 'type', 'foreign_id', 'foreign_type', 'created_at', 'updated_at'].member?(k)
end
end
end
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.use_instantiated_fixtures = false
config.mock_with :rspec
# Use FactoryGirl for fixtures
config.include FactoryGirl::Syntax::Methods
# Auto-detect spec types
config.infer_spec_type_from_file_location!
# Insert devise helpers in controller specs
config.include Devise::TestHelpers, type: :controller
config.include Warden::Test::Helpers
config.extend ControllerMacros, type: :controller
config.include ControllerMacros
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = 'random'
config.before(:suite) do
Warden.test_mode!
# Clean all tables to start
DatabaseCleaner.clean_with :truncation
# Use transactions for tests
DatabaseCleaner.strategy = :transaction
# Truncating doesn't drop schemas, ensure we're clean here, app *may not* exist
Apartment::Tenant.drop('test') rescue nil
# Create the default tenant for our tests
Account.create!(name: 'Test', domain: 'test', email: 'info#example.com')
end
config.before(:each) do
# Start transaction for this test
DatabaseCleaner.start
# Switch into the default tenant
Apartment::Tenant.switch! 'test'
# Use Timecop to freeze times on time-critical tests
Timecop.return
end
config.after(:each) do
# Reset tentant back to `public`
Apartment::Tenant.reset
# Rollback transaction
DatabaseCleaner.clean
end
end
Update
OK, I think the problem lies elsewhere:
After creating models with FactoryGirl (like FactoryGirl.create(:project)), the records aren't available in the controller. If I write something like
#foo = Project.all.to_json
in my controller and want to display this data in my view, I just get [] (after calling save_and_open_page).
I though FactoryGirl.create writes data directly to the DB? Why isn't the data available in my controller methods?
Update 2
Adding
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
solves this problem, but now I get:
Failure/Error: user = FactoryGirl.create(:user)
ActiveRecord::RecordNotUnique:
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_users_on_lastname"
DETAIL: Key (lastname)=(Kautzer) already exists.
: INSERT INTO "users" ("email", "initial", "firstname", "lastname", "encrypted_password", "archived", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"
I thought the database gets cleaned after each test?
Update 3
Disregard, the last problem was a mistake in my database setup.
Adding
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
to my spec_helper.rb solves the problem :)
I'm newbie to Rails Testing.
After following some tutorial online, I could able to setup and run testing for Model.
But when trying to test for Controller, Testing was failed as it is redirected to login page.
I've tried every instruction I can find on web to sign in for devise and still couldn't able to sign in and move forward.
Appreciate if someone could help and give me a direction to move forward.
AwardedBidsControllerTest
test_should_get_index FAIL (0.45s)
MiniTest::Assertion: Expected response to be a <:success>, but was <302>
Below is my setup
test/test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/rails"
require "minitest/reporters"
Minitest::Reporters.use!(
Minitest::Reporters::SpecReporter.new,
ENV,
Minitest.backtrace_filter
)
class ActiveSupport::TestCase
fixtures :all
include FactoryGirl::Syntax::Methods
end
class ActionController::TestCase
include Devise::TestHelpers
end
test/controllers/awarded_bids_controller_test.rb
require "test_helper"
class AwardedBidsControllerTest < ActionController::TestCase
test "should get index" do
user = create(:user)
sign_in user
get :index
assert_response :success
end
end
app/controllers/awarded_bids_controller.rb
class AwardedBidsController < ApplicationController
before_filter :authenticate_user!
def index
#awarded_bids = AwardedBid.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #awarded_bids }
end
end
end
test/factories/users.rb
#using :login instead of :email.
FactoryGirl.define do
factory :user do |u|
u.login "user"
u.name "Normal"
u.surname "User"
u.password "Abc2011"
end
end
Below is the version info.,
JRuby 1.7.21(Ruby 1.9.3)
Rails 3.2.22
Devise 3.0.4
Minitest 4.7.5
From integration_helpers.rb, for example:
class PostsTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
test 'authenticated users can see posts' do
sign_in users(:bob)
get '/posts'
assert_response :success
end
end
In your test_helper.rb file's ActiveSupport::TestCase class, add a new method log_in_as like this:
require "test_helper"
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/rails"
require "minitest/reporters"
Minitest::Reporters.use!(
Minitest::Reporters::SpecReporter.new,
ENV,
Minitest.backtrace_filter
)
class ActiveSupport::TestCase
fixtures :all
include FactoryGirl::Syntax::Methods
# Returns true if a test user is logged in.
def is_logged_in?
!session[:user_id].nil?
end
# Logs in a test user.
def log_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
if integration_test?
post login_path, session: { email: user.email,
password: password,
remember_me: remember_me }
else
session[:user_id] = user.id
end
end
private
# Returns true inside an integration test.
def integration_test?
defined?(post_via_redirect)
end
end
class ActionController::TestCase
include Devise::TestHelpers
end
Then, use this log_in_as method instead of sign_in in your test:
require "test_helper"
class AwardedBidsControllerTest < ActionController::TestCase
test "should get index" do
user = create(:user)
log_in_as user
get :index
assert_response :success
end
end
I got problem with my Capybara test. When I run spec with Selenium support (js: true) it goes through the instructions until it encounters click_on method, which hits create controller action and returns error message: "ActionController::UnknownFormat at /category/items
ActionController::UnknownFormat", which is probably respond_with(#items) issue. In fact, create method works fine as well as whole functionality on the server, but into test it crashes. What is more, item creates in the database, so that confirms create method works properly.
Request information:
GET No GET data
POST commit "Do AJAX request"
item {"item_id"=>"12345"}
utf8 "✓"
COOKIES cookie_policy "accepted"
Spec:
require 'spec_helper'
feature "Test in Capybara and Selenium", js: true do
background(:all) do
...seed data
end
scenario "my test scenario" do
visit root_path
visit '/category/1'
within first(".css_style") do
click_on("item_link")
end
# we are into item view
click_on("Do AJAX request") # error
visit another_view_path
end
end
Controller:
class ...::ItemsController < ...::BaseController
respond_to :html, except: ['create', ...]
respond_to :js, only: ['create', ...]
def create
...
#item.save
respond_with(#item)
end
Views:
thing view:
<%= link_to("request", category_items_path(item: {thing_id: thing}), method: :post, class: 'add_item_button', remote: true, name: "animate_item") %>
create.js.erb:
if($('#add-item-button').length > 0){
location.reload(200);
}
spec_helper.rb
...
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
require 'capybara/rails'
require 'selenium-webdriver'
require 'rack/utils'
Capybara.app = Rack::ShowExceptions.new(Sklep::Application)
Capybara.register_driver :selenium do |app|
Capybara::Selenium::Driver.new(app, :browser => :chrome)
end
Capybara.default_driver = :selenium
...
If I forgot about something please let me know.