Showing posts with label rspec. Show all posts
Showing posts with label rspec. Show all posts

Wednesday, October 17, 2018

Rails Devise API - Login route responds with `You need to sign in or sign up before continuing.`

1 comment

I'm currently using Devise with my Rails API app to authenticate users using devise-jwt.

This is what my User model looks like:

class User < ApplicationRecord   devise :database_authenticatable,          :registerable,          :jwt_authenticatable,          jwt_revocation_strategy: JWTBlackList end 

And config/routes.rb is set up like this:

Rails.application.routes.draw do   devise_for :users,          path: '',          path_names: {            sign_in: 'login',            sign_out: 'logout',            registration: 'signup'          },          controllers: {            sessions: 'sessions',            registrations: 'registrations'          } end 

This is the sessions controller:

class SessionsController < Devise::SessionsController    private    def respond_with(resource, _opts = {})     render json: resource   end    def response_to_on_destroy     head :no_content   end end 

and the registrations controller:

class RegistrationsController < Devise::RegistrationsController   respond_to :json    def create     build_resource(sign_up_params)      resource.save     render_resource(resource)   end end 

I ran some Rspec tests shown below which were successful -

require 'rails_helper'  RSpec.describe SessionsController, type: :request do   let(:user) { create(:user) }   let(:url) { '/login' }   let(:params) do     {       user: {         email: user.email,         password: user.password       }     }   end    context 'when params are correct' do     before do       post url, params: params     end      it 'returns 200' do       expect(response).to have_http_status(200)     end      it 'returns JTW token in authorization header' do       expect(response.headers['Authorization']).to be_present     end      it 'returns valid JWT token' do       decoded_token = decoded_jwt_token_from_response(response)       expect(decoded_token.first['sub']).to be_present     end   end end 

But when I run the following POST request to /login on Postman I get the following message:

postman request

On the right hand side you are able to see the rails console and server, showing the credentials are correct, but still we get 401

Any clues on what might be wrong? It's been difficult finding good resources on Devise using Rails API.

Thank you in advance

1 Answers

Answers 1

After digging through the SessionsController I found the reason it returned 401: warden was trying to authenticate an empty body. Fixed this by added Content-Type to the request header

Read More

Monday, February 5, 2018

Rails 5 rspec test failing for uploaded file stub

Leave a Comment

In my rspec for uploaded files I am stubbing a function like this:

 allow(ABCCLass::XYZProcessor).to receive(:prepare_processing).with(csv_statements).and_return(nil) 

and

let(:csv_statements) do  [fixture_file_upload('transaction.csv', 'application/csv')] end 

I get error in expect: expected Rack::Test::UploadedFile got ActionDispatch::Http::UploadedFile

This makes total sense as fixture_file_upload returns Rack::Test::UploadedFile and stub is generated with it but in controller uploaded file is instance of Action::Dispatch::Uploaded file.

Any idea how can I create stub and fix this issue?

Backtrace:

-[[#<Rack::Test::UploadedFile:0x00559d3f298100 @content_type="application/csv", @original_filename="blank.csv", @tempfile=#<Tempfile:/tmp/blank.csv20180114-293-jcb2ii>, @_filename="eur-transactions.csv">,    -  #<Rack::Test::UploadedFile:0x00559d3f296e40 @content_type="application/csv", @original_filename="blank.csv", @tempfile=#<Tempfile:/tmp/blank.csv20180114-293-1xbpd0t>, @_filename="gbp-statement.csv">,    -  #<Rack::Test::UploadedFile:0x00559d3f295e50 @content_type="application/csv", @original_filename="blank.csv", @tempfile=#<Tempfile:/tmp/blank.csv20180114-293-1peng22>, @_filename="internal_HSBCnet1.xls">]]    +[[#<ActionDispatch::Http::UploadedFile:0x00559d3cb3a6a8 @tempfile=#<Tempfile:/tmp/RackMultipart20180114-293-1l9ma69.csv>, @original_filename="eur-transactions.csv", @content_type="application/csv", @headers="Content-Disposition: form-data; name=\"bank_statements[]\"; filename=\"eur-transactions.csv\"\r\nContent-Type: application/csv\r\nContent-Length: 0\r\n">,    +  #<ActionDispatch::Http::UploadedFile:0x00559d3cb3a608 @tempfile=#<Tempfile:/tmp/RackMultipart20180114-293-17oxrrk.csv>, @original_filename="gbp-statement.csv", @content_type="application/csv", @headers="Content-Disposition: form-data; name=\"bank_statements[]\"; filename=\"gbp-statement.csv\"\r\nContent-Type: application/csv\r\nContent-Length: 0\r\n">,    +  #<ActionDispatch::Http::UploadedFile:0x00559d3cb3a568 @tempfile=#<Tempfile:/tmp/RackMultipart20180114-293-2ukuul.xls>, @original_filename="internal_HSBCnet1.xls", @content_type="application/csv", @headers="Content-Disposition: form-data; name=\"bank_statements[]\"; filename=\"internal_HSBCnet1.xls\"\r\nContent-Type: application/csv\r\nContent-Length: 0\r\n">]]      Please stub a default value first if message might be received with other args as well.   # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-support-3.7.0/lib/rspec/support.rb:97:in `block in <module:Support>'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-support-3.7.0/lib/rspec/support.rb:106:in `call'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-support-3.7.0/lib/rspec/support.rb:106:in `notify_failure'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.7.0/lib/rspec/mocks/error_generator.rb:327:in `notify'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.7.0/lib/rspec/mocks/error_generator.rb:311:in `__raise'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.7.0/lib/rspec/mocks/error_generator.rb:60:in `raise_missing_default_stub_error'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.7.0/lib/rspec/mocks/proxy.rb:206:in `raise_missing_default_stub_error'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.7.0/lib/rspec/mocks/proxy.rb:191:in `message_received'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.7.0/lib/rspec/mocks/proxy.rb:326:in `message_received'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.7.0/lib/rspec/mocks/method_double.rb:77:in `proxy_method_invoked'  # /root/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-mocks-3.7.0/lib/rspec/mocks/method_double.rb:64:in `block (2 levels) in define_proxy_method'  # ./app/controllers/admin/daily_adjustments/bank_transfer_deposits_controller.rb:25:in `start_process' 

1 Answers

Answers 1

Instead of calling fixture_file_upload('transaction.csv', 'application/csv') in the test, you should put that return value instead.

I would use an instance double if you want to stub it. Something like:

let(:csv_statements) { instance_double(Rack::Test::UploadedFile, content_type: 'application/csv', original_filename: 'blank.csv', file_name: 'eur-transactions.csv') } 

This might not work via copy and paste but this will stub an INSTANCE of that class and whenever those messages are received, it will return those values. I don't know what happens inside of "fixture_file_upload", but whatever Rack::Test::UploadedFile object is returned, just stub it like so. If you want to do a class stub, it's very similar:

let(:csv_statements) { class_double(Rack::Test::UploadedFile, content_type: 'application/csv', original_filename: 'blank.csv', file_name: 'eur-transactions.csv').as_stubbed_const } 
Read More

Sunday, February 4, 2018

Rspec and commit callback giving an incorrect result

Leave a Comment

I have a User and Role model:

class User < ApplicationRecord    has_and_belongs_to_many :roles   validates :roles, presence: true, on: :save   after_create_commit :assign_role    private    def assign_role     self.roles << Role.client if roles.empty?   end end  class Role < ApplicationRecord    has_and_belongs_to_many :users   validates :name, presence: true, uniqueness: true   scope :client, -> { where(name: :client) } end 

I can't get the following Rspec to pass:

  describe '#assign_role' do     context 'when creating a new user' do       it 'is triggered', test: true do         user = build(:user)         expect(user).to receive(:assign_role)         user.run_callbacks(:commit)       end     end   end (#<User id: nil, email: "tester1@example.com", created_at: nil, updated_at: nil>).assign_role(*(any args))  expected: 1 time with any arguments  received: 0 times with any arguments 

I know the method is nit being called because I put binding.pry inside the method.

However if I change the callback in the model to after_create and :create in the rspec test it works

describe '#assign_role' do     context 'when creating a new user' do       it 'is triggered', test: true do         user = build(:user)         expect(user).to receive(:assign_role)         user.run_callbacks(:create)       end     end   end 

I have even tried to turn off transactional tests with self.use_transactional_tests = false but it does not change the result. I want to use after_create_commit instead of after_create.

How can I get this test to pass?

rspec 3.7, rails 5.2.beta2

1 Answers

Answers 1

Please try this gem gem "test_after_commit" for that, You might be thinking, “That’s weird. Why do I have to use a whole separate gem to test a callback that comes with Rails? Shouldn’t it just happen automatically?”

You’re right. It is weird. But it won’t stay weird for long.

Once Rails 5 update, you won’t have to worry about test_after_commit.

please see this link

Read More

Monday, December 11, 2017

How do I mock a class using Rspec and Rails?

Leave a Comment

I'm using Rails 5 with Rspec 3. How do I mock a class in my Rspec method? I have the following class

require 'rails_helper'  describe CryptoCurrencyService do    describe ".sell" do      it "basic_sell" do       last_buy_price = 3000       last_transaction = MoneyMakerTransaction.new({         :transaction_type => "buy",         :amount_in_usd => "100",         :btc_price_in_usd => "#{last_buy_price}"       })       @client = Coinbase::Wallet::Client.new(api_key: ENV['COINBASE_KEY'], api_secret: ENV['COINBASE_SECRET'])       sell_price = 4000       assert sell_price > last_buy_price * (1 + MoneyMakerThreshhold.find_buy.pct_change)        allow(@client).to receive(:sell_price).and_return({"base"=>"BTC", "currency"=>"USD", "amount"=>"#{sell_price}"})        svc = CryptoCurrencyService.new       svc.sell(last_transaction)       last_transaction = MoneyMakerTransaction.find_latest_record       assert last_transaction.transaction_type, "sell"     end    end  end 

Instead of actually instantiating the class "Coinbase::Wallet" in the line

@client = Coinbase::Wallet::Client.new(api_key: ENV['COINBASE_KEY'], api_secret: ENV['COINBASE_SECRET']) 

I'd like to create mock taht I could then insert into my service class, which I'm testing. As it stands right now, when I run things, the actual underlying class is getting instantiated, resulting the run time error ...

  1) CryptoCurrencyService.sell basic_sell      Failure/Error: payment_method = client.payment_methods()[0]       Coinbase::Wallet::AuthenticationError:        invalid api key 

3 Answers

Answers 1

rspec mocks and stubs can be used on any class. For example:

coinbase_mock = double(api_key: ENV['COINBASE_KEY'], api_secret: ENV['COINBASE_SECRET']) expect(Coinbase::Wallet::Client).to_receive(:new).and_return(coinbase_mock) 

then you can add whatever you like to the coinbase_mock so that it quacks like the class you need... :)

Answers 2

You could use a Ruby Struct like this:

Coinbase::Wallet::Client = Struct.new(:api_key, :api_secret) @client = Coinbase::Wallet::Client.new(ENV['COINBASE_KEY'], ENV['COINBASE_SECRET']) @client.api_key #=> whatever was in ENV['COINBASE_KEY'] 

Then pass that object in.

If you need behavior on it you can also get that like this:

Coinbase::Wallet::Client = Struct.new(:api_key, :api_secret) do   def client_info     ## logic here     "info"   end end  @client = Coinbase::Wallet::Client.new(ENV['COINBASE_KEY'], ENV['COINBASE_SECRET']) @client.client_info #=> "info" 

Answers 3

Preferred RSpec (since ver. 3) style would be

let(:coinbase_client) { instance_double(Coinbase::Wallet::Client) }  # validates that mocked/stubbed methods present in class definitiion  before do   allow(coinbase_client).to receive(:sell_price).and_return({"base"=>"BTC", "currency"=>"USD", "amount"=>"PRICE YOU PROVIDE"}) end 

docs about instance_double method

while you inject coinbase_client as a construction parameter to your classes that use it internally

OR if for some reasons you can't use dependancy injection, you could mock any instance of Coinbase::Wallet::Client with

allow_any_instance_of(Coinbase::Wallet::Client).to receive(... *mock specific method*) 
Read More

Tuesday, November 21, 2017

Stub authentication in request specs

Leave a Comment

I'm looking for the way to do this but in request specs. I need to log in and log out a double or instance_double to Devise instead of an actual ActiveModel/ActiveRecord.

By using the code in the wiki page:

module RequestSpecHelpers     def sign_in(user = double('user'))       if user.nil?         allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user})         allow(controller).to receive(:current_user).and_return(nil)       else         allow(request.env['warden']).to receive(:authenticate!).and_return(user)         allow(controller).to receive(:current_user).and_return(user)       end     end   end 

I get this error: undefined method 'env' for nil:NilClass

I saw this question and this wiki, but if I want to use doubles of the user those two don't work. I was using the last one, works fine with a real user but with a double it doesn't log it in.

The tests:

RSpec.describe 'new shipment', type: :request do   describe 'authenticated as user' do     before do       @user = double(:user, id: 1, email: 'user@gmail.com', password: 'password',                       id_card: '4163649-1', first_name: 'Jane', last_name: 'Doe')        sign_in @user     end   end end 

If I include:

RSpec.configure do |config|   config.include Devise::TestHelpers, :type => :requests end 

I get this error:

Failure/Error: @request.env['action_controller.instance'] = @controller       NoMethodError:        undefined method `env' for nil:NilClass      # /root/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/devise-4.3.0/lib/devise/test/controller_helpers.rb:40:in `setup_controller_for_warden' 

Problem with Frederick Cheung answer

If I do that the login_asmethod doesn't fail but it doesn't really log the user in. So when I try to access a path that has a before_action :authenticate_user! callback it fails.

Here is my code based on his answer:

require 'rails_helper'  RSpec.describe 'new shipment', type: :request do   describe 'authenticated as user' do     include Warden::Test::Helpers      before(:each) do       Warden.test_mode!       #stub more methods as needed by the pages you are testing       user = instance_double(User, to_key: 1, authenticatable_salt: 'example')       login_as(user, scope: 'user')     end      it 'returns 200 Ok' do       get new_shipment_path       expect(response).to have_http_status(:ok)     end   end end 

And this is the response when running rspec:

 1) new shipment authenticated as user returns 200 Ok      Failure/Error: expect(response).to have_http_status(:ok)        expected the response to have status code :ok (200) but it was :found (302)      # ./spec/requests/shipments_requests_spec.rb:41:in `block (3 levels) in <top (required)>' 

As you can see instead of allowing me to access the path it redirects me, this is the usual behavior when the user is not allowed to access the path.

It I change the instance_double for a real User saved in the database this approach works correctly:

# only changed this line in the before hook user = User.create(email: 'user@gmail.com', password: 'password',id_card: '4163649-1', first_name: 'Jane', last_name: 'Doe') 

Result:

Finished in 3.23 seconds (files took 33.47 seconds to load) 1 example, 0 failures 

1 Answers

Answers 1

It sounds like you're using Devise 3.x ( since Devise::TestHelpers was renamed in devise 4), Devise::TestHelpers is only designed to work with controller specs.

If you can upgrade to devise 4, it has separate helpers for request specs and controller tests. This is just a very thin wrapper around what warden provides, which hides all the messing around with env.

There are some extra complications when using a double - you need to stub out various methods devise calls that you might not realise.

The following worked for me

describe 'example' do   include Warden::Test::Helpers    before(:each) do     Warden.test_mode!     #stub more methods as needed by the pages you are testing     user = instance_double(User, to_key: 1, authenticatable_salt: 'example')     login_as(user, scope: 'user')   end end 
Read More

Sunday, July 9, 2017

RSpec 2.3 + Devise 1.0.11

Leave a Comment

I've got a really old Rails 2.3.18, ruby 1.9.3, rspec 1.x application which we are upgrading and it had restful-authentication in it. So I've replaced that with Devise 1.0.11.

I can login to the application, but my tests will not run;

Here is the test in question

require 'spec_helper'  describe CategoriesController do   context "As a logged in user" do     before do       login_user       current_firm = mock_model(Firm, :id => 1)       controller.stub!(:current_firm).and_return(current_firm)     end      describe "#index" do       it "should render index" do         get :index         response.should render_template('index')       end     end    end end 

Here is the error I get;

NoMethodError in 'CategoriesController As a logged in user#index should render index' You have a nil object when you didn't expect it! You might have expected an instance of ActiveRecord::Base. The error occurred while evaluating nil.[]= /home/map7/code/pdfcat/spec/spec_helper.rb:18:in `login_user' spec/controllers/categories_controller_spec.rb:6:in `block (3 levels) in <top (required)>' 

The error happens on this line;

[20, 29] in /usr/local/rbenv/versions/1.9.3-p551/lib/ruby/gems/1.9.1/gems/warden-0.10.7/lib/warden/session_serializer.rb    20        key    21      end    22      23      def store(user, scope)    24        return unless user => 25        session[key_for(scope)] = serialize(user)    26      end 

The problem is 'session' is nil when I'm at this point.

I've pushed the full code to here: https://github.com/map7/pdfcat/tree/devise

My plan was to get devise working in the tests then I could jump to Rails 3.0 and continue the upgrade.

1 Answers

Answers 1

There's an old message in google groups that I think is relevant: https://groups.google.com/forum/#!topic/rspec/4AHuPtHFD34

It recommends using this:

before do   request.env['warden'].stub(:authenticate!) { double(User) } end 

I'd probably put it in rails_helper.rb so that it runs for all tests

Read More

Wednesday, June 7, 2017

Rspec test for when an I18n key is missing?

Leave a Comment

How do you simulate a missing I18n key in an Rspec test? I have a helper that looks to use a I18n key if its available. I don't know how to create the test case for when it is not available. Based on console testing I'm pretty sure the helper method is correct, but I don't know how to construct the test against that outcome (when all three possible title types are not set).

# app/helpers/application_helper.rb  def available_page_title   title = ""   case   when content_for?(:page_title)     title = content_for(:page_title)   when !@page_title.nil?     title = @page_title   when I18n.exists?('page_title.default')     title = t('page_title.default')   end end 

--

# config/locales/en.yml  en:   page_title:     delimiter: ' - '     default: Default Page Title     admin_namespace: 'Admin'   # spec/helpers/application_helper.rb describe 'available_page_title' do   it '(1) returns the the translation title if no other titles set' do     allow(I18n).to receive(:t).with('page_title.default').and_return("Test Title from i18n")     @page_title = nil     expect(helper.available_page_title).to eq(t('page_title.default'))   end    it '(2) returns the the @page_title if it exists and no content_for exists' do     allow(I18n).to receive(:t).with('page_title.default').and_return("Test Title from i18n")     translation_title = t('page_title.default')     @page_title = "Test Title from @page_title"     expect(helper.available_page_title).to eq(@page_title)   end    it '(3) returns the the content_for title if it exists' do     allow(I18n).to receive(:t).with('page_title.default').and_return("Test Title from i18n")     translation_title = t('page_title.default')     @page_title = "Test Title from @page_title"     helper.content_for(:page_title, 'Test Title from content_for')     expect(helper.available_page_title).to eq('Test Title from content_for')   end    it '(4) returns a blank string if no titles are found' do     # Things I've tried...     # I18n.backend.store_translations(:en, { page_title: { default: '' }})     # I18n.backend.store_translations(:en)     # I18n.backend.store_translations(:en, { nil })     # I18n.backend.store_translations()     # I18n.backend = I18n::Backend::Simple.new     allow(I18n).to receive(:t).with('page_title.default').and_return(nil)     @page_title = nil     expect(helper.available_page_title).to eq('')   end end 

Here's the test results:

$ bundle exec rspec spec/helpers/application_helper_spec.rb:130 Run options: include {:locations=>{"./spec/helpers/application_helper_spec.rb"=>[130]}}  Randomized with seed 14478  ApplicationHelper   available_page_title     (3) returns the the content_for title if it exists     (1) returns the the translation title if no other titles set     (2) returns the the @page_title if it exists and no content_for exists     (4) returns a blank string if no titles are found (FAILED - 1)  Failures:    1) ApplicationHelper available_page_title should (4) return a blank string if no titles are found      Failure/Error: expect(helper.available_page_title).to eq('')         expected: ""             got: "Default Page Title"         (compared using ==)      # ./spec/helpers/application_helper_spec.rb:160:in `block (3 levels) in <top (required)>'  Finished in 0.69197 seconds (files took 4.32 seconds to load) 4 examples, 1 failure  Failed examples:  rspec ./spec/helpers/application_helper_spec.rb:152 # ApplicationHelper available_page_title should (4) return a blank string if no titles are found  Randomized with seed 14478 

Update 2017-05-31

@gwcodes's answer helped a little but still didn't get the test set up properly.

allow(I18n).to receive(:translate).with('page_title.default').and_return(nil) 

Using that line, the test is set up so that there actually is still a key page_title.default, but its value is nil. That's not useful.

What my helper method checks for is the existence of the key. Here's the pry output of the current test situation:

[5] pry(#<RSpec::ExampleGroups::ApplicationHelper::AvailablePageTitle>)> I18n.translate('page_title.default') => nil [6] pry(#<RSpec::ExampleGroups::ApplicationHelper::AvailablePageTitle>)> I18n.exists?('page_title.default') => true [7] pry(#<RSpec::ExampleGroups::ApplicationHelper::AvailablePageTitle>)> I18n.t('page_title.default').empty? => false 

So the question still remains. How do you set the test environment so that a check for the existence of an I18n key returns false?

2 Answers

Answers 1

I did come up with a solution by stubbing an I18n Backend for testing (although it does seem a little like the ball peen hammer approach).

I created spec/support/i18n_simple_stub.rb:

module I18n   module Backend     class SimpleStub       def exists?(key, *args)         false       end     end   end end 

and then set the backend in that (previously failing) test. Here's the new test:

it '(4) returns a blank string if no titles are found' do   # Sets exists? method to false   I18n.backend = I18n::Backend::SimpleStub.new    @page_title = nil   expect(helper.available_page_title).to eq('') end 

If anyone has a better method, I'd love to hear it and learn.

Answers 2

I am thinking to use your first solution into a shared context.

# spec/support/i18n_stubs.rb  RSpec.shared_context 'i18n stubs' do   def stub_i18n_value_with_nil(key)     allow(I18n).to receive(:t).with(key).and_return(nil)     allow(I18n).to receive(:exists?).with(key).and_return(false)   end end 

Then add require support/i18n_stubs on your spec helper file. Inside wherever your spec file needs it, you can just simply put include_context 'i18n stubs' and call stub_i18n_value_with_nil('some.i18n.key') before doing assertions.

For example in spec file:

require 'spec_helper'  RSpec.describe 'some_file_or_class' do   include_context 'i18n stubs'    describe 'available_page_title' do     context 'when i18n key is not present' do       before(:each) do         stub_i18n_value_with_nil('some.i18n.key')       end        it 'returns blank string' do         # do assertion here       end     end   end end 
Read More

Monday, June 5, 2017

How to configure RSpec to load monkey patched classes

Leave a Comment

I have extended the string class as follows:

class String   def last_character     self[-1]   end end 

I have places the string.rb file in the lib as follows:

lib/core_extensions/string.rb 

I have tested the setup and I can use the last_character method in a Rails console.

However, when I run an RSpec test for a class that uses the extended String class it gives me an error:

undefined method `last_character' for " ":String 

Do I have to tell RSpec to load these class extension files somehow?

2 Answers

Answers 1

One way is to eagerly/explicitly load your custom extensions in the rails_helper.rb

Dir[Rails.root.join('lib/core_extensions/*.rb')].each { |f| require f } 

Answers 2

Does your spec have require "rails_helper"?

Have you tried restarting Spring?

Read More

Saturday, May 20, 2017

Runnin rspec from IntelliJ

Leave a Comment

I'm usally running my specs from inside IntelliJ. I've deleted my gems and reinstalled them using bundle install (due to another error) and now I'm getting an error when trying to run the specs.

I've noticed that running the specs from intelliJ it uses:

    from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require' 

which isn't the case when running from the shell (I placed a print inside the kernel_require script check it).

Also I've see that the ruby version from intelliJ is:

"ruby 2.2.4: 230" 

and from shell:

ruby -e 'print "ruby #{ RUBY_VERSION }p#{ RUBY_PATCHLEVEL }"' ruby 2.2.6p396%     

The error:

    /home/user/.rvm/rubies/ruby-2.2.4/bin/ruby -e $stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) /home/user/.rvm/gems/ruby-2.2.4/bin/rspec /home/user/workspace/auto-test/spec/pools/pool_cg_view_spec.rb --require teamcity/spec/runner/formatter/teamcity/formatter --format Spec::Runner::Formatter::TeamcityFormatter     Testing started at 10:21 ...     /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require': incompatible library version - /home/user/.rvm/gems/ruby-2.2.4/gems/nokogiri-1.6.8/lib/nokogiri/nokogiri.so (LoadError)         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/gems/ruby-2.2.4/gems/nokogiri-1.6.8/lib/nokogiri.rb:32:in `rescue in <top (required)>'         from /home/user/.rvm/gems/ruby-2.2.4/gems/nokogiri-1.6.8/lib/nokogiri.rb:28:in `<top (required)>'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/gems/ruby-2.2.4/gems/capybara-2.7.1/lib/capybara.rb:3:in `<top (required)>'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/gems/ruby-2.2.4/gems/capybara-2.7.1/lib/capybara/dsl.rb:2:in `<top (required)>'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/gems/ruby-2.2.4/gems/capybara-2.7.1/lib/capybara/rspec.rb:2:in `<top (required)>'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:128:in `require'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:128:in `rescue in require'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:39:in `require'         from /home/user/workspace/auto-test/spec/support/capybara.rb:3:in `<top (required)>'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/workspace/auto-test/spec/spec_helper.rb:6:in `block in <top (required)>'         from /home/user/workspace/auto-test/spec/spec_helper.rb:6:in `each'         from /home/user/workspace/auto-test/spec/spec_helper.rb:6:in `<top (required)>'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/configuration.rb:1295:in `block in requires='         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/configuration.rb:1295:in `each'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/configuration.rb:1295:in `requires='         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/configuration_options.rb:109:in `block in process_options_into'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/configuration_options.rb:108:in `each'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/configuration_options.rb:108:in `process_options_into'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/configuration_options.rb:21:in `configure'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/runner.rb:101:in `setup'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/runner.rb:88:in `run'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/runner.rb:73:in `run'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/lib/rspec/core/runner.rb:41:in `invoke'         from /home/user/.rvm/gems/ruby-2.2.4/gems/rspec-core-3.4.1/exe/rspec:4:in `<top (required)>'         from /home/user/.rvm/gems/ruby-2.2.4/bin/rspec:23:in `load'         from /home/user/.rvm/gems/ruby-2.2.4/bin/rspec:23:in `<top (required)>'         from -e:1:in `load'         from -e:1:in `<main>'      Process finished with exit code 1 

If I try to run the specs from shell it will work, no errors, so I guess the problem is related to the configuration of the run but not sure what.

The run configuration which worked until now:

enter image description here

I've tried to remove the nokogiri gem and reinstall it, but still the same.

Any ideas?

project structure: enter image description here

1 Answers

Answers 1

You are pointing out that you are in ruby 2.2.6 when using the console. IntelliJ is complaining about missing the nokogiri gem.

I can imagine that probably your last bundle install did not use ruby 2.2.4 (as used by IntelliJ) but ruby 2.2.6 to install the gems.

I recommend the following steps to narrow this down:

  • verify and ensure that you are using ruby 2.2.4 on the shell (this is very important!)
  • run bundle install to ensure all gems of your project are installed for ruby 2.2.4
  • run your specs in the shell

If this succeeds then try running specs with IntelliJ. This should work in case the steps described above went fine.

A personal note (not knowing if it's already the case): In my projects I always have a .ruby-version file in the root of my project to ensure the correct ruby version for the current project. You may even go a step further and add a .ruby-gemset file.

More information can be found in the rvm docs

Read More

Saturday, February 25, 2017

Rails 3 to 4 migration uniqueness validation issues

Leave a Comment

Context

We are migrating from Rails 3.2.12 to 4.0.2 and Ruby 1.9.3 to 2.1.8.

We have a lot of test coverage to accomplish the migration in the form of RSpec.

Issue

One of the spec that checks that a uniqueness validation on a Card model is failing.

validates :mobile, uniqueness: {scope: :program_member_id, message: I18n.t('models.card.error.cardholder_already_has_mobile')}, if: :mobile 

Where a program_member may only have one mobile: true card.

The spec creates 2 cards for the member, turns one into a mobile card, then expects the validation's message when doing so with the second card.

let(:program) { FactoryGirl.create(:program) } let(:card) { FactoryGirl.create(:card, program: program) }  context 'when cardholder already has a mobile card' do   it 'fails validation' do     card2 = FactoryGirl.create(:card, program: program)     program_member_user = FactoryGirl.create(:program_member_user, card_number: card2.cardnumber)     program_member = program_member_user.program_members.first      program_member.cards << card2     card2.update_attributes(:mobile => true)      program_member.cards << card     card.update_attributes(:mobile => true)      expect(card.errors.messages).to include(:mobile=>[I18n.t('models.card.error.cardholder_already_has_mobile')])   end end 

Expectation:

expected {} to include {:mobile=>["Cardholder already has a mobile card"]} 

When I go to our master branch, this spec passes.

The only factor that has changed from this spec working to failing is the Rails 3 to 4 migration.

Tried running the spec code in console only to find the member has 2 mobile cards and doing card.valid? returns true for both instances.

Question

Has anything changed in Rails 4 in regards to uniqueness validation or validation life cycle?

1 Answers

Answers 1

Alright so I'm onto something.

I created a test project using the same Ruby and Rails version.

https://github.com/frank184/test_uniquness

In this project, I would have a User model that has an admin column as a boolean with a similar validation.

validates_uniqueness_of :admin, if: :admin? 

I used shoulda-matchers and rspec to describe the desired outcome.

require 'rails_helper'  RSpec.describe User, type: :model do   let(:user) { build :user }   subject { user }    describe 'validations' do     context 'when admin = true' do       before(:each) { user.admin = true }       it { is_expected.to validate_uniqueness_of(:admin)  }     end   end end 

The spec failed with the following output:

Failures:    1) User validations when admin = true should validate that :admin is case-sensitively unique      Failure/Error: it { is_expected.to validate_uniqueness_of(:admin)  }         User did not properly validate that :admin is case-sensitively unique.          After taking the given User, whose :admin is ‹true›, and saving it as          the existing record, then making a new User and setting its :admin to          ‹true› as well, the matcher expected the new User to be invalid, but          it was valid instead.      # ./spec/models/user_spec.rb:10:in `block (4 levels) in <top (required)>'  Finished in 0.11435 seconds (files took 0.79997 seconds to load) 1 example, 1 failure 

I decided that the code was good and bumped Rails to 4.1.0 exactly.

The spec passed!

bundle update rspec .  Finished in 0.09538 seconds (files took 1.28 seconds to load) 1 example, 0 failures 
Read More

Sunday, September 25, 2016

Authenticated Route not working for Rspec test

Leave a Comment

I'm following this post about setting up authentication in the routes of my Rails 4 application.

Here is my routes.rb file:

Rails.application.routes.draw do    devise_for :employees, :controllers => { registrations: 'employees/registrations' }   devise_for :clients     authenticate :employee do     resources :quotation_requests, only: [:show, :edit,:index, :update, :destroy]   end    resources :quotation_requests, only: [:new, :create]    get '/dashboard' => 'dashboard#show', as: 'show_dashboard'    root to: 'home#index' end 

Here is my quotation_requests_controller_spec.rb file:

require 'rails_helper'  RSpec.describe QuotationRequestsController, type: :controller do       describe "GET index" do         it "renders :index template" do             get :index             expect(response).to render_template(:index)         end          it "assigns quotation requests to template" do             quotation_requests = FactoryGirl.create_list(:quotation_request, 3)             get :index             expect(assigns(:quotation_requests)).to match_array(quotation_requests)         end      end      describe "GET edit" do         let(:quotation_request) { FactoryGirl.create(:quotation_request)}          it "renders :edit template" do             get :edit, id: quotation_request             expect(response).to render_template(:edit)         end         it "assigns the requested quotation request to template" do             get :edit, id: quotation_request             expect(assigns(:quotation_request)).to eq(quotation_request)         end     end      describe "PUT update" do         let(:quotation_request) { FactoryGirl.create(:quotation_request)}          context "valid data" do             new_text = Faker::Lorem.sentence(word_count=500)             let(:valid_data) { FactoryGirl.attributes_for(:quotation_request, sample_text: new_text)}              it "redirects to quotation_request#showtemplate" do                 put :update, id: quotation_request, quotation_request: valid_data                 expect(response).to redirect_to(quotation_request)             end             it "updates quotation request in the database" do                 put :update, id: quotation_request, quotation_request: valid_data                 quotation_request.reload #need to reload the object because we have just updated it in the database so need to get the new values                 expect(quotation_request.sample_text).to eq(new_text)             end         end          context "invalid data" do             let(:invalid_data) { FactoryGirl.attributes_for(:quotation_request, sample_text: "", number_of_words: 400)}              it "renders the :edit template" do                 put :update, id: quotation_request, quotation_request: invalid_data                 expect(response).to render_template(:edit)             end             it "does not update the quotation_request in the database" do                 put :update, id: quotation_request, quotation_request: invalid_data                 quotation_request.reload                 expect(quotation_request.number_of_words).not_to eq(400)             end         end     end      describe "GET new", new: true do         it "renders :new template" do             get :new             expect(response).to render_template(:new)         end         it "assigns new QuotationRequest to @quotation_request" do             get :new             expect(assigns(:quotation_request)).to be_a_new(QuotationRequest)         end     end      describe "GET show" do          #this test requires that there be a quotation request in the database         let(:quotation_request) { FactoryGirl.create(:quotation_request) }          context 'invalid request' do              it "does not render :show template if an employee or client is not signed in" do                  #setup                 quotation_request =  create(:quotation_request)                  #exercise                 get :show, id: quotation_request                  #verification                 expect(response).to_not render_template(:show)              end          end          context 'valid request' do              sign_in_proofreader              it "renders :show template if an employee or client is signed in" do                  #setup                 quotation_request =  create(:quotation_request)                   #exercise                 get :show, id: quotation_request                  #verification                 expect(response).to render_template(:show)             end              it "assigns requested quotation_request to @quotation_request" do                 get :show, id:  quotation_request                 expect(assigns(:quotation_request)).to eq(quotation_request)             end          end     end      describe "POST create", post: true do         context "valid data" do              let(:valid_data) {FactoryGirl.nested_attributes_for(:quotation_request)}              it "redirects to quotation_requests#show" do                 post :create, quotation_request: valid_data                 expect(response).to redirect_to(quotation_request_path(assigns[:quotation_request]))             end              it "creates new quotation_request in database" do             expect {                 post :create, quotation_request: valid_data                 }.to change(QuotationRequest, :count).by(1)             end         end          context "invalid data" do         let(:invalid_data) {FactoryGirl.nested_attributes_for(:quotation_request).merge(sample_text: 'not enough sample text')}              it "renders :new template" do                 post :create, quotation_request: invalid_data                 expect(response).to render_template(:new)             end              it "doesn't creates new quotation_request in database" do                 expect {                     post :create, quotation_request: invalid_data                 }.not_to change(QuotationRequest, :count)             end         end     end      describe "DELETE destroy" do          let(:quotation_request) { FactoryGirl.create(:quotation_request) }          it "redirects to the quotation request#index" do             delete :destroy, id: quotation_request             expect(response).to redirect_to(quotation_requests_path)         end         it "delets the quotation request from the database" do             delete :destroy, id: quotation_request             expect(QuotationRequest.exists?(quotation_request.id)).to be_falsy         end      end end 

My quotation_requests_controller.rb

class QuotationRequestsController < ApplicationController # before_action :authenticate_employee!, :only => [:show]      def index         @quotation_requests =  QuotationRequest.all     end      def new         @quotation_request = QuotationRequest.new         @quotation_request.build_client     end      def edit         @quotation_request = QuotationRequest.find(params[:id])     end      def create       client = Client.find_or_create(quotation_request_params[:client_attributes])       @quotation_request = QuotationRequest.new(quotation_request_params.except(:client_attributes).merge(client: client))       if @quotation_request.save         ClientMailer.quotation_request_created(client.email, @quotation_request.id).deliver_now         redirect_to @quotation_request, notice: 'Thank you.'       else         render :new       end     end      def show         @quotation_request = QuotationRequest.find(params[:id])     end      def update         @quotation_request = QuotationRequest.find(params[:id])         if @quotation_request.update(quotation_request_params)             redirect_to @quotation_request         else             render :edit         end     end      def destroy         QuotationRequest.destroy(params[:id])         redirect_to quotation_requests_path     end      private      def quotation_request_params         params.require(:quotation_request).permit(:number_of_words, :return_date, :sample_text, :client_attributes => [:first_name, :last_name, :email])     end  end 

I know the routes authentication works because if I test them in the browser I get redirected to the sign_in page. However, the tests don't pass in Rspec.

if I put this code in the quotation_requests_controller.rb:

 before_action :authenticate_employee!, :only => [:show] 

The rspec tests pass. So for some reason Rspec does not register the authentication of the routes.

Here is the output from Rspec for the tests run with the authenticated routes:

QuotationRequestsController   GET index     valid request       renders :index template for signed in employee       assigns quotation requests to template     invalid request       does not render :index template without a signed in employee (FAILED - 1)   GET edit     valid request       renders :edit template with a signed in employee       assigns the requested quotation request to template     invalid request       does not render the :edit template without a signed in employee (FAILED - 2)   PUT update     valid request       valid data         redirects to quotation_request#showtemplate         updates quotation request in the database       invalid data         renders the :edit template         does not update the quotation_request in the database     invalid request       redirects user to the sign in page (FAILED - 3)   GET new     renders :new template     assigns new QuotationRequest to @quotation_request   GET show     invalid request       does not render :show template if an employee or client is not signed in (FAILED - 4)     valid request       renders :show template if an employee or client is signed in       assigns requested quotation_request to @quotation_request   POST create     valid data       redirects to quotation_requests#show       creates new quotation_request in database     invalid data       renders :new template       doesn't creates new quotation_request in database   DELETE destroy     valid request       redirects to the quotation request#index       delets the quotation request from the database     invalid request       does not delete the quotation request without a signed in employee (FAILED - 5)  Failures:    1) QuotationRequestsController GET index invalid request does not render :index template without a signed in employee      Failure/Error: expect(response).to_not render_template(:index)        Didn't expect to render index      # ./spec/controllers/quotation_requests_controller_spec.rb:43:in `block (4 levels) in <top (required)>'      # -e:1:in `<main>'    2) QuotationRequestsController GET edit invalid request does not render the :edit template without a signed in employee      Failure/Error: expect(response).to_not render_template(:edit)        Didn't expect to render edit      # ./spec/controllers/quotation_requests_controller_spec.rb:92:in `block (4 levels) in <top (required)>'      # -e:1:in `<main>'    3) QuotationRequestsController PUT update invalid request redirects user to the sign in page      Failure/Error: expect(response).to_not redirect_to(quotation_request)        Didn't expect to redirect to #<QuotationRequest:0x007fe7eb69c8c0>      # ./spec/controllers/quotation_requests_controller_spec.rb:182:in `block (4 levels) in <top (required)>'      # -e:1:in `<main>'    4) QuotationRequestsController GET show invalid request does not render :show template if an employee or client is not signed in      Failure/Error: expect(response).to_not render_template(:show)        Didn't expect to render show      # ./spec/controllers/quotation_requests_controller_spec.rb:217:in `block (4 levels) in <top (required)>'      # -e:1:in `<main>'    5) QuotationRequestsController DELETE destroy invalid request does not delete the quotation request without a signed in employee      Failure/Error: expect(QuotationRequest.exists?(quotation_request.id)).to be_truthy         expected: truthy value             got: false      # ./spec/controllers/quotation_requests_controller_spec.rb:361:in `block (4 levels) in <top (required)>'      # -e:1:in `<main>'  Finished in 2.11 seconds (files took 1.75 seconds to load) 23 examples, 5 failures  Failed examples:  rspec ./spec/controllers/quotation_requests_controller_spec.rb:37 # QuotationRequestsController GET index invalid request does not render :index template without a signed in employee rspec ./spec/controllers/quotation_requests_controller_spec.rb:83 # QuotationRequestsController GET edit invalid request does not render the :edit template without a signed in employee rspec ./spec/controllers/quotation_requests_controller_spec.rb:171 # QuotationRequestsController PUT update invalid request redirects user to the sign in page rspec ./spec/controllers/quotation_requests_controller_spec.rb:208 # QuotationRequestsController GET show invalid request does not render :show template if an employee or client is not signed in rspec ./spec/cont 

Why do the routes I have written not work in Rspec tests?

1 Answers

Answers 1

I take it you are using rspec-rails in your rails app. Rspec-rails sets up a lot of convenience methods for you, but it also introduces some black-magic, which can lead to some unexpected results - like this.

As you can see here it is explained in the comments for controller specs:

# Supports a simple DSL for specifying behavior of ApplicationController. # Creates an anonymous subclass of ApplicationController and evals the # `body` in that context. Also sets up implicit routes for this # controller, that are separate from those defined in "config/routes.rb". 

I guess the logic here is, controller features are different from routing and should be tested separately (and indeed rspec-rails offers a test group for routing), so we do not need the routes for controller specs, meaning you should be able to test your controller without setting up the routes.

In my oppinion, testing the redirect for unauthenticated users is more of an integration test, since it requires multiple parts of your application to work together and as such should not be tested in the controller context, but rather as a feature in some blackbox test.

You can write integration tests by placing them in one of these directories spec/requests, spec/api, and spec/integration or by explicitely declaring their type with

RSpec.describe "Something", type: :request do 

or place it in spec/features or declare the type as

RSpec.describe "Something", type: :feature do 

depending on which level you want to test the redirect (meaning: only test the request-response cycle, or run it in a simulated browser). Please refer to the documentation for integration tests on the rspec-rails github page for more information.

Read More

Wednesday, April 13, 2016

Rspec fails when testing view edit form

Leave a Comment

I'm working on my first rails app here and two of the generated tests don't pass:

Failures:    1) gardens/edit renders the edit garden form      Failure/Error: assert_select "inputgarden_name[name=?]", "garden[name]"       Minitest::Assertion:        Expected at least 1 element matching "inputgarden_name[name="garden[name]"]", found 0..        Expected 0 to be >= 1.      # ./spec/views/gardens/edit.html.haml_spec.rb:20:in `block (3 levels) in <top (required)>'      # ./spec/views/gardens/edit.html.haml_spec.rb:18:in `block (2 levels) in <top (required)>'    2) gardens/new renders new garden form      Failure/Error: assert_select "inputgarden_name[name=?]", "garden[name]"       Minitest::Assertion:        Expected at least 1 element matching "inputgarden_name[name="garden[name]"]", found 0..        Expected 0 to be >= 1.      # ./spec/views/gardens/new.html.haml_spec.rb:19:in `block (3 levels) in <top (required)>'      # ./spec/views/gardens/new.html.haml_spec.rb:17:in `block (2 levels) in <top (required)>'  Finished in 1.15 seconds (files took 2.74 seconds to load) 34 examples, 2 failures, 17 pending  Failed examples:  rspec ./spec/views/gardens/edit.html.haml_spec.rb:15 # gardens/edit renders the edit garden form rspec ./spec/views/gardens/new.html.haml_spec.rb:14 # gardens/new renders new garden form 

I'm not sure why this is. When I look at the test, I'm kind of surprised the path doesn't contain an id to edit (something like /gardens/#{@garden.id}/edit). When I try to edit the test accordingly rspec fails to run telling me that @garden isn't instantiated yet.

spec/views/gardens/edit.html.haml_spec.rb:

  1 require 'rails_helper'   2   3   4 RSpec.describe "gardens/edit", type: :view do   5   before(:each) do   6     @garden = assign(:garden, Garden.create!(   7       :name => "MyString",   8       :square_feet => 1,   9       :zone => 1,  10       :garden_type => "MyString",  11       :user => nil  12     ))  13   end  14  15   it "renders the edit garden form" do  16     render  17  18      assert_select "form[action=?][method=?]", garden_path(@garden), "post" do  19   20        assert_select "input#garden_name[name=?]", "garden[name]"  21   22        assert_select "input#garden_square_feet[name=?]", "garden[square_feet]"  23   24        assert_select "input#garden_zone[name=?]", "garden[zone]"  25   26        assert_select "input#garden_garden_type[name=?]", "garden[garden_type]"  27   28        assert_select "input#garden_user_id[name=?]", "garden[user_id]"  29      end  30   end  31 end 

What do I have to do to make these tests pass?

EDIT: here's the edit views

/app/views/gardens/edit.html.haml:

%h1 Editing garden  = render 'form'  = link_to 'Show', @garden \| = link_to 'Back', gardens_path 

/app/views/gardens/_form.html.haml:

= simple_form_for(@garden) do |f|   = f.error_notification    .form-inputs     = f.input :name     = f.input :square_feet     = f.input :zone     = f.input :garden_type     = f.association :user    .form-actions     = f.button :submit 

1 Answers

Answers 1

The error message that you included indicates that an html element for the given matcher was not found.

For associations Simple Form gem generates select elements instead of input elements by default. https://github.com/plataformatec/simple_form#associations

You need to change the last assertion to: assert_select "select#garden_user_id[name=?]", "garden[user_id]"

I am guessing the issue is occurring in the spec for the new view?

Read More