Why is this RSpec test failing? - ruby-on-rails

I'm in the process of learning Ruby on Rails, so treat me like a total neophyte, because I am.
I've got a User model with some associated RSpec tests, and the following test fails:
require 'spec_helper'
describe User do
it 'should require a password' do
User.new({:email => 'valid_email#example.com', :password => '', :password_confirmation => ''}).should_not be_valid
end
end
The relevant part of the User model looks like this:
class User < ActiveRecord::Base
...
validates :password, :presence => true,
:confirmation => true,
:length => { :minimum => 6 }
...
end
Here's the catch: if I run User.new(...).valid? from a Rails console using the arguments above, it returns false as expected and shows the correct errors (password is blank).
I was using spork/autotest and I restarted both to no avail, but this test also fails even running it directly with rspec. What am I doing wrong here?
EDIT
I tried a few more things with the test. This fails:
u = User.new({:email => 'valid_email#example.com', :password => '', :password_confirmation => ''})
u.should_not be_valid
So does this:
u = User.new({:email => 'valid_email#example.com', :password => '', :password_confirmation => ''})
u.valid?
u.errors.should_not be_empty
This passes, confirming that :password is indeed blank:
u = User.new({:email => 'valid_email#example.com', :password => '', :password_confirmation => ''})
u.password.should == ''

So, it's actually spork that is causing the problem. You can turn caching off, so that it won't need restarting every time :
http://ablogaboutcode.com/2011/05/09/spork-testing-tip-caching-classes
I think this is what happens :
ruby-1.9.2-p180 :020 > u = User.new
=> #<User id: nil, email: ...
ruby-1.9.2-p180 :021 > u.errors
=> {}
ruby-1.9.2-p180 :022 > u.save
=> false
ruby-1.9.2-p180 :023 > u.errors
=> {:email=>["can't be blank", "can't be blank"], ...}
In short, if you change new to create, it will work :) I think that this happens because the matcher be_valid checks on the model validation errors. There can be a deeper explanation, but i think that if you use create instead of new, it will work.
EDIT : I have a be_valid_verbose version that might help. Just create a 'be_valid_verbose.rb' file in your rspec/custom_matchers folder, and inside it write :
RSpec::Matchers.define :be_valid_verbose do
match do |model|
model.valid?
end
failure_message_for_should do |model|
"#{model.class} expected to be valid but had errors:n #{model.errors.full_messages.join("n ")}"
end
failure_message_for_should_not do |model|
"#{model.class} expected to have errors, but it did not"
end
description do
"be valid"
end
end
Now check against be_valid_verbose instead of be_valid. It will hopefully present you with some more information on what is happening in your case.

As I feared, the answer was stupidity. This was a spork problem. I thought I had killed the existing process and was running rspec independently, but I later found the spork process still running in a different shell, and rspec had been connecting to it all along. Restarting spork (or killing it entirely) and re-running the tests fixed the problem.
I found this particularly deceptive in that rspec continually updated the test output to reflect the fact that it was aware of my test changes, so it appeared to me that it was running against up-to-date code. Now I'm left to wonder what the real utility of spork is, since apparently I can't trust that it's actually running the right tests correctly.

Related

Gibbon::GibbonError at /visitors You must set an api_key prior to making a call

Running OSX Mavericks, ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-darwin13.0], rvm 1.25.23 (master), and rails-4.1.0 (allegedly)
I'm working through the railsapps.org book on learning rails I have finished implementing the mailchimp email list code, and when I press submit on my form, I get the following error:
Gibbon::GibbonError at /visitors
You must set an api_key prior to making a call
I was working through an invalid URI error, and it mysteriously disappeared over the weekend (I haven't touched the Mac since last Friday). Now I have this new error.
My API Key and List ID are valid and properly set. If I look back in the log, I see another error that the mailchimp.lists.subscribe method is missing.
Here's the code as implemented from the book:
class Visitor < ActiveRecord::Base
has_no_table
column :email, :string
validates_presence_of :email
validates_format_of :email, :with => /\A[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i
def subscribe
mailchimp = Gibbon::API.new
result = mailchimp.lists.subscribe({
:id => Rails.application.secrets.mailchimp_list_id,
:email => {:email => self.email},
:double_optin => false,
:update_existing => true,
:send_welcome => true
})
Rails.logger.info("Subscribed #{self.email} to MailChimp") if result
end
end
I hate being a noob when I can't debug for myself... Replies are appreciated.
Regards,
Jeff
Gibbon automatically looks for the environment variable MAILCHIMP_API_KEY and Gibbon will use it when you create an instance. If you haven't set a Unix env variable MAILCHIMP_API_KEY, you will need to set it explicitly. To hardcode it:
mailchimp = Gibbon::API.new("your_api_key")
Or obtain it from the config/secrets.yml file:
mailchimp = Gibbon::API.new(Rails.application.secrets.mailchimp_api_key)

RSpec before suite not being run

I'm trying to stub any external API calls in my test suite, but the before(:suite) is never executed. Webmock always reports that I need to stub the maps.googleapis.com even though no tests have been run yet (no green dots, no red Fs).
spec_helper.rb:
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
...
config.before(:suite) do
puts "THIS NEVER SHOWS"
stub_request(:get, "maps.googleapis.com").
with(headers: {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
to_return(status: 200, body: "", headers: {})
end
The geocoder gem ends up trying to save the lat/lon from googleapis.com and an error is raised by Webmock saying that the URL is unregistered.
EDIT: Error snippet:
$ bundle exec rspec spec/factories_spec.rb
/home/jake/.rvm/gems/ruby-2.1.0#global/gems/webmock-1.17.4/lib/webmock/http_lib_adapters/net_http.rb:114:in `request': Real HTTP connections are disabled. Unregistered request: GET http://maps.googleapis.com/maps/api/geocode/json?address=[private]&language=en&sensor=false with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'} (WebMock::NetConnectNotAllowedError)
You can stub this request with the following snippet:
stub_request(:get, "http://maps.googleapis.com/maps/api/geocode/json?address=[private]&language=en&sensor=false").
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})
============================================================
from /home/jake/.rvm/gems/ruby-2.1.0#global/gems/geocoder-1.1.9...
...
Again, I'll stress that this has to do with the fact that the code in the config.before(:each) block is never run. Why? Because if it was, I could "raise 'WTF'" and 'WTF' should appear in the console output instead of the error you see above. I only see 'WTF' when I "un-bundle" the Webmock gem.
Well I was doing "something cute" with my RSpec tests by creating tests at runtime depending on whether or not the Factory has an attribute that is a file. Due to the way my factories/models were set up, factories were being created (saved) when the attributes for a certain factory were being read, so the block of code that's generating the tests runs outside of RSpec's config.before(:suite) and WebMock raises the error.
https://github.com/bblimke/webmock/issues/378
Moreover, here's specifically what I was doing wrong - not related to WebMock:
1) In my factories.rb, I was calling create() for associations which may not yet exist. Why? Because RSpec was giving me errors saying "[association] was blank". It was doing that because I had validates_presence_of :association_id instead of just :association. When I used create() instead of build(), it "worked". Of course when it came time to use WebMock, I was creating (and thus saving) objects calling geocoder to do it's thing. The solution was to fix validates_presence_of to use the right attribute and use build() instead of create() in my factories.
Bad Example:
# In spec/factories.rb
factory :review, class: Manager::Review do
rating 4
wine { Manager::Wine.first || create(:wine) }
reviewer { Manager::Reviewer.first || create(:reviewer) }
date Time.now
association :referral, referrable_id: 1, referrable_type: Manager::Review, strategy: :build
end
# In app/models/manager/review.rb
validates_presence_of :rating_id, :wine_id, :reviewer_id, :date
Good Example:
# In spec/factories.rb
factory :review, class: Manager::Review do
rating 4
wine { Manager::Wine.first || build(:wine) }
reviewer { Manager::Reviewer.first || build(:reviewer) }
date Time.now
association :referral, referrable_id: 1, referrable_type: Manager::Review, strategy: :build
end
# In app/models/manager/review.rb
validates_presence_of :rating, :wine, :reviewer, :date
2) FWIW, I told geocoder to fetch the geocode before_save, not after_validate like it suggests in their home page.
Also, you cannot stub with WebMock in the before(:suite), but it works in before(:each)

is there an ordinal to cardinal function in ruby or rails?

I'm trying to find a better way in expressing my cucumbers so I am looking for an ordinal to cardinal function that converts this:
When I fill up the first passenger field
Then I should see the passenger list update with the first passenger details
When I follow "Add Another Passenger"
Then I should see a second passenger field
When I fill up the second passenger field
Then I should see the passenger list update with the second passenger details
into something more dynamic(instead of creating separate steps for each line)
here's a sample of my web steps
When /^I fill up the first passenger field$/ do
fill_in("booking_passengers_attributes_0_first_name", :with => "Blah")
fill_in("booking_passengers_attributes_0_last_name", :with => "blah")
select("5' to 6'", :from => "booking_passengers_attributes_0_height")
select("100 to 150lbs", :from => "booking_passengers_attributes_0_weight")
end
When /^I fill up the second passenger field$/ do
fill_in("booking_passengers_attributes_1_first_name", :with => "Wee")
fill_in("booking_passengers_attributes_1_last_name", :with => "Sir")
select("5' to 6'", :from => "booking_passengers_attributes_1_height")
select("150 to 200lbs", :from => "booking_passengers_attributes_1_weight")
end
See that 0 and 1? I wish to convert "first" to a cardinal number so i can just substitute. You can also just suggest a better way to declare the cukes :)
UPDATED ANSWER
I am in the middle of refactoring but basically I used 1st instead of first and used to_i on that.
When /^I fill up the "([^"]*)" passenger field$/ do |id|
input_id = id.to_i - 1
fill_in("booking_passengers_attributes_#{input_id}_first_name", :with => id)
fill_in("booking_passengers_attributes_#{input_id}_last_name", :with => "Passenger")
select("5' to 6'", :from => "booking_passengers_attributes_#{input_id}_height")
select("100 to 150lbs", :from => "booking_passengers_attributes_#{input_id}_weight")
end
i dont really completely understand,exactly what you want to do, but you can do something like this with active support:
1.ordinalize # => "1st"
2.ordinalize # => "2nd"
1002.ordinalize # => "1002nd"
and there is a action view helper number_in_words to get "first" , "second" etc
i don't know much about cukes sorry,
Use the short-form, easily-parsable ordinal:
When /^I fill up the (\d+)(?:st|nd|rd|th) passenger field$/ do |n|
# etc...
end
This function is built into Chronic:
irb(main):001:0> require 'chronic'
=> true
irb(main):002:0> Chronic::Numerizer.numerize("eighty-fifth").to_i
=> 85

Rails is not inputting user data to postgresql

I am having an issue with Rails not inputting values to postgresql. The database itself is connected. When I run db:create:all (snippet from database.yml)
development:
adapter: postgresql
encoding: unicode
database: website_development
username: postgres
password: *******
host: 127.0.0.1
port: 9435
(test: is the same but with database: website_test instead of website_development) all the databases are created for test and development. When I run my db:migration the user table is also created e.g. snippet from migration file "date"_create_user.rb
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :username
t.string :email
t.timestamps
end
end
def self.down
drop_table :users
end
end
(I have checked in pgAdmin and found the tables that where created)But when I try to insert data from the console e.g.(this was run in sandbox)
irb(main):001:0> User.create!(:username => "John", :email => "john#example.com)
=> #<User id: 1, username: nil, email: nil, created_at: "2011-04-26 22:00:28", u
pdated_at: "2011-04-26 22:00:28">
here is the sql produced on a different create! I had run
[1m[35mSQL (2.0ms)[0m INSERT INTO "users" ("username", "email", "created_at", "updated_at") VALUES (NULL, NULL, '2011-04-26 20:53:43.363908', '2011-04-26 20:53:43.363908') RETURNING "id"
Any help as to why rails is creating the databases and tables fine but can't find the proper username and email to enter into sql.
P.S. I am running Rspec for my tests and have made several tests regarding the values of username and email not being nil to which all succeed.
......................
Finished in 1.62 seconds
22 examples, 0 failures
Notification failed: 201 - The destination server was not reachable
Notification failed: 201 - The destination server was not reachable
As you can see all Rspec tests are green but it to is having trouble connecting to the postgres server
Thank you in advance for any advice.
Update: added user model snippet
class User < ActiveRecord::Base
attr_accessor :username, :email
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
username_regex = /\A[\w\d]+\z/i
validates :username, :presence => true,
:format => { :with => username_regex },
:length => { :maximum => 30},
:uniqueness => { :case_sensitive => false }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
end
==Answer==
These were my mistakes:
Part 1: By changing attr_accessor to attr_accessible all my tests worked properly, and everything that needed to went to red, this also allowed me to add :email details but not :username details which leads to part 2.
Part 2: For some reason rails didn't like the fact that my table was named :user and my column was named :username. So I tried changing :username to :loginname which fixed the problem entirely.
Thank you everyone for all your help.
To isolate this you may want to construct a unit test to replicate the problem, then repair it as required. At first I suspected it would be a case of protected attributes, but it appears you have made them accessible, which is the correct thing to do.
Calling create! directly is somewhat hazardous as you are not easily able to capture the object that is half-created in the event of an exception. This is because although the exception contains a reference to a model, it is not clear if the User model or some other model caused the exception in the first place without additional digging.
A more reliable approach is this:
def test_create_example
user = User.new(:username => "John", :email => "john#example.com")
assert_equal 'John', user.username
assert_equal 'john#example.com', email
user.save
assert_equal [ ], user.errors.full_messages
assert_equal false, user.new_record?
end
If an error occurs in the validation stream you will see the error listed alongside what should be an empty array. It also checks that the record has been saved by testing that it is no longer a new record as records can be valid but fail to save if a before_save or before_create filter returns false, something that happens by accident quite often.
If you call new and then save you have an opportunity to inspect the newly prepared object before it is saved, as well as after.

Factory Girl created objects not clearing in between tests?

I'm being held up implementing tests with a slight confusion. With User.create I can create and save in multiple tests:
should "test something" do
u1 = User.create(:first_name => "Fred", :last_name => "Flintstone")
assert true
end
should "test something else" do
u1 = User.create(:first_name => "Fred", :last_name => "Flintstone")
assert true
end
but using Factory.create, it throws a ActiveRecord duplicate entry error:
should "test something" do
Factory.create(:amanda_levy)
assert true
end
should "test something else" do
Factory.create(:amanda_levy)
assert true
end
Error: "ActiveRecord::StatementInvalid: Mysql::Error: Duplicate entry"
What gives?
Do you have the the following line in your spec_helper:
config.use_transactional_fixtures = true
That tells rspec/ test::unit to start a transaction at the start
of every test case, and issue a rollback when it's over.

Resources