Test failure gives incomplete error message when using short syntax - ruby

There are 2 syntaxes (afaik) for writing tests with RSpec:
The classic/old way:
describe "when user_id is not present" do
before { #micropost.user_id = nil }
it "should not be valid" do
#micropost.should_not be_valid
end
end
That gives this error when failing:
rspec ./spec/models/micropost_spec.rb:19 # Micropost when user_id is
not present should not be valid
And the short syntax:
describe "when user_id is not present" do
before { #micropost.user_id = nil }
it { should_not be_valid }
end
That gives this error when failing:
rspec ./spec/models/micropost_spec.rb:18 # Micropost when user_id is
not present
The last one is missing the "should not be valid" part.
Is there any way to have the complete test failure message, maybe a flag I don't know about?
Note: A more complete error message is still there no matter what:
1) Micropost when user_id is not present
Failure/Error: it { should_not be_valid }
expected #<Micropost id: nil, content: "Lorem ipsum", user_id: nil, created_at: nil, updated_at: nil> not to be valid
# ./spec/models/micropost_spec.rb:18:in `block (3 levels) in <top (required)>'
But the recap at the end is incomplete.

The text you see in
rspec ./spec/models/micropost_spec.rb:19 # Micropost1 when user_id is not present2 should not be valid3
and
rspec ./spec/models/micropost_spec.rb:18 # Micropost1 when user_id is not present2 3
is not the error message, but the test name, which is a concatenation of the current test's description with all its parents' descriptions:
describe Micropost do # <= 1
describe "when user_id is not present" do # <= 2
before { #micropost.user_id = nil }
it "should not be valid" do # <= 3
#micropost.should_not be_valid
end
end
end
In the short (one-liner) syntax - the test itself does not have a description
describe Micropost do # <= 1
describe "when user_id is not present" do # <= 2
before { #micropost.user_id = nil }
it { should_not be_valid } # <= 3 - not described!
end
end
If you want to force a description on this test you could write it like this:
it('should not be valid') { should_not be_valid }
But this kind of beats the purpose of the one-liner, which should be self-explanatory, don't you think?

Related

Tests fail with record creation in Rails 5

I am building an app in school and I am running into this error. As of right now the app walk through was started in rails 4.2.6, and I am running 5.0.0.1.
The error is:
Failures:
1) Post Creation can be created
Failure/Error: expect(#post).to be_valid
expected #<Post id: nil, date: "2016-12-20", rationale: "Anything", created_at: nil, updated_at: nil, user_id: nil> to be valid, but got errors: User must exist
# ./spec/models/post_spec.rb:10:in `block (3 levels) in <top (required)>'
Finished in 0.65569 seconds (files took 2.19 seconds to load)
10 examples, 1 failure
Failed examples:
rspec ./spec/models/post_spec.rb:9 # Post Creation can be created
My code is as follows. I have compared to the repo on the walk-through and it matches perfectly. What am i missing?
require 'rails_helper'
RSpec.describe Post, type: :model do
describe "Creation" do
before do
#post = Post.create(date: Date.today, rationale: "Anything")
end
it "can be created" do
expect(#post).to be_valid
end
it "cannot be created without a date and rationale" do
#post.date = nil
#post.rationale = nil
expect(#post).to_not be_valid
end
end
end
Rails 5 differs from Rails 4 in that when you have a belongs_to relation, Rails 5 will automatically validate the presence of the associated object, even without you adding any validations.
Probably your Post model belongs to a User. Because of this, you need to create a user in your test setup, or the validation will fail:
describe "Creation" do
before do
#user = User.create( ... )
#post = Post.create(date: Date.today, rationale: "Anything", user: #user)
end
it "can be created" do
expect(#post).to be_valid
end
it "cannot be created without a date and rationale" do
#post.date = nil
#post.rationale = nil
expect(#post).to_not be_valid
end
end

Custom assert message for multiple expect statements within match

I have written a custom match method in Rspec that matches an object against a hash. What I am trying to do is set custom failure messages for each line of the expect.
describe "/cars" do
car = FactoryGirl.create(:car, name: 'Alpha')
describe car do
it "displays single items" do
get cars_path
parsed_response = JSON.parse(response.body)
record_hash = parsed_response['cars'][0]
is_expected.to be_a_car_match_of(record_hash)
end
end
end
RSpec::Matchers.define :be_a_car_match_of do |hash|
match do |car|
expect(car.id).to eq(hash['id'])
expect(car.name).to eq(hash['name'])
end
failure_message do |car|
"expected that #{car} would be a match of #{hash}"
end
end
So what I would like is to have something like the following:
RSpec::Matchers.define :be_a_car_match_of do |hash|
match do |car|
expect(car.id).to eq(hash['id']) 'ids did not match'
expect(car.name).to eq(hash['name']) 'names did not match'
end
end
This would print out a much clearer error message.
I was initially doing this in mini-test but for a variety of reasons (outside of my control) needed to change it to rspec. The code I had in mini-test was:
def assert_car(car, hash)
assert_equal car.id, hash['id'], "ids did not match"
assert_equal car.name, hash['name'], "names did not match"
end
This is what I am trying to replicate.
Here is another example that requires less setup:
require 'rspec/expectations'
RSpec::Matchers.define :be_testing do |expected|
match do |actual|
expect(5).to eq(5)
expect(4).to eq(5)
end
failure_message do
"FAIL"
end
end
describe 'something' do
it 'something else' do
expect("expected").to be_testing('actual')
end
end
When this example is run, "FAIL" is printed out. On the other hand if I had:
describe 'something' do
it 'something else' do
expect(4).to eq(5)
end
end
I would get the following error message:
expected: 5
got: 4
This is what I want. I want to know what part of the custom matcher failed.
You could call the low-level matches? method instead of expect. Something like this:
require 'rspec/expectations'
RSpec::Matchers.define :be_a_positive_integer do
m1, m2 = nil, nil # matchers
r1, r2 = false, false # results
match do |actual|
m1 = be_a Integer # this returns matcher instances
m2 = be > 0
r1 = m1.matches?(actual) # evaluate matchers
r2 = m2.matches?(actual)
r1 && r2 # true if both are true
end
failure_message do |actual| # collect error messages from matchers
messages = []
messages << m1.failure_message unless r1
messages << m2.failure_message unless r2
messages.join("\n")
end
end
describe -1 do
it { is_expected.to be_a_positive_integer }
end
describe 1.0 do
it { is_expected.to be_a_positive_integer }
end
describe -1.0 do
it { is_expected.to be_a_positive_integer }
end
Output:
Failures:
1) -1 should be a positive integer
Failure/Error: it { is_expected.to be_a_positive_integer }
expected: > 0
got: -1
# ./ruby_spec.rb:24:in `block (2 levels) in <top (required)>'
2) 1.0 should be a positive integer
Failure/Error: it { is_expected.to be_a_positive_integer }
expected 1.0 to be a kind of Integer
# ./ruby_spec.rb:28:in `block (2 levels) in <top (required)>'
3) -1.0 should be a positive integer
Failure/Error: it { is_expected.to be_a_positive_integer }
expected -1.0 to be a kind of Integer
expected: > 0
got: -1.0
# ./ruby_spec.rb:32:in `block (2 levels) in <top (required)
I think aggregate_failures is what you are looking for :
It wraps a set of expectations with a block. Within the block, expectation failures will not immediately abort like normal; instead, the failures will be aggregated into a single exception that is raised at the end of the block, allowing you to see all expectations that failed.
See : https://relishapp.com/rspec/rspec-expectations/docs/aggregating-failures

Testing with Rspec codeschool level 3 challenge 5

I have been battling this test way too long and I am not sure where I am stuck. Here is the model I am trying to test:
class Zombie < ActiveRecord::Base
attr_accessible :iq
validates :name, presence: true
def genius?
iq >= 3
end
def self.genius
where("iq >= ?", 3)
end
end
Here is what I am working with to start:
describe Zombie do
context "with high iq" do
let(:zombie) { Zombie.new(iq: 3, name: 'Anna') }
subject { zombie }
it "should be returned with genius" do
Zombie.genius.should include(zombie)
end
it "should have a genius count of 1" do
Zombie.genius.count.should == 1
end
end
end
This is the part of the refactor that is working:
it { should be_genius }
#it "should have a genius count of 1" do
# Zombie.genius.count.should == 1
#end
Here is where I am currently stuck at with the refactor:
describe Zombie do
context "with high iq" do
let!(:zombie) { Zombie.new(iq: 3, name: 'Anna') }
subject { zombie }
it {should include("zombie")}
it { should be_genius }
end
end
According to the examples this should work, but no matter what I try it keeps bombing on the include. I know I am missing something lame here. Thoughts or tips anyone?
Current Error Message:
Failures:
1) Zombie with high iq
Failure/Error: it {should include("zombie")}
NoMethodError:
undefined method `include?' for #<Zombie:0x00000006792380>
# zombie_spec.rb:7:in `block (3 levels) '
Finished in 0.12228 seconds
2 examples, 1 failure
Failed examples:
rspec zombie_spec.rb:7 # Zombie with high iq
You need to add the ! to the let and change new to create in order to save the record.
describe Zombie do
context "with high iq" do
let!(:zombie) { Zombie.create(iq: 3, name: 'Anna') }
subject { zombie }
it "should be returned with genius" do
Zombie.genius.should include(zombie)
end
it "should have a genius count of 1" do
Zombie.genius.count.should == 1
end
end
end
I'm not sure what examples you are referring to that suggest your refactor should work, but the implicit subject zombie used in your first refactored example is an ActiveRecord instance and the include matcher you're using is intended to be used with a string, array or hash as described in https://www.relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/include-matcher.
As for your second refactored example, I gather it's working because you've only indicated a problem with the include.

FactoryGirl doesn't write data

I am so confused as to why this is happening...
I'm using factory girl, and this factory:
# spec/factories/partners/contact.rb
FactoryGirl.define do
factory :partner_contact, class: Partners::Contact do
first_name "Josh"
last_name { Faker::Name.last_name }
email { Faker::Internet.email }
partner
end
end
But when I run my rspec it says
Mysql2::Error: Field 'first_name' doesn't have a default value: INSERT INTO `partner_contact` (`created_on`) VALUES ('2014-01-30 22:21:53')
Here is the spec that I'm using that generates the above error.
# spec/models/contact.rb
require 'spec_helper'
require 'pp'
describe Partners::Contact do
it "has a valid factory" do
partner = FactoryGirl.create(:partner_contact)
# partner.should be_valid
puts "MEOW MEOW"
end
it "is invalid without a firstname" do
# FactoryGirl.build(:partner_contact, first_name: nil).should_not be_valid
end
it "is invalid without a lastname" do
# FactoryGirl.build(:partner_contact, last_name: nil).should_not be_valid
end
it "is invalid without an email address" do
# FactoryGirl.build(:partner_contact, email: nil).should_not be_valid
end
#it "returns a contact's fullname as a string"
end
As you can see, it's not trying to commit any data that I listed, first_name, last_name or email. Just 'created_on' which is generated via rails.
What's going on here?
Do you have any fixtures for that table? If fixtures are present and enabled, Rails will attempt to load them into your database before your test is evaluated. Look in spec/fixtures.
Your setup seems wrong. Check the FactoryGirl Rails gem. Don't forget to configure RSpec to use FactoryGirl as well.

Issue with Failures in Chapter 7 of Hartl Tutorial

When I run bundle exec rspec spec/ I'm getting 21 examples and 3 failures. Those failures being:
Failures:
1) User has_password? method should be true if the passwords match
Failure/Error: #user.has_password?(#attr[:password]).should be_true
NoMethodError:
undefined method has_password?' for nil:NilClass
# ./spec/models/user_spec.rb:47:inblock (3 levels) in '
2) User has_password? method should be false if the passwords don't match
Failure/Error: #user.has_password?("invalid").should be_false
NoMethodError:
undefined method has_password?' for nil:NilClass
# ./spec/models/user_spec.rb:51:inblock (3 levels) in '
3) User password validations should accept valid email addresses
Failure/Error: it "should reject invalid email addresses" do
NoMethodError:
undefined method it' for #<RSpec::Core::ExampleGroup::Nested_3::Nested_3:0x00000102eb38b0>
# ./spec/models/user_spec.rb:97:inblock (3 levels) in '
I'll post my user_spec.rb file bc I think it's almost right, but not completely. Note the commented out ends, I had those in play before but thought they were wrong so commented them out.
require 'spec_helper'
describe User do
before(:each) do
#attr = {
:name => "Example User",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar" }
end
it "should create a new instance given valid attributes" do
User.create!(#attr)
end
describe "password encryption" do
before(:each) do
#user = User.create!(#attr)
end
it "should have an encrypted password attribute" do
#user.should respond_to(:encrypted_password)
end
it "should set the encrypted password" do
#user.encrypted_password.should_not be_blank
end
end
describe "has_password? method" do
it "should be true if the passwords match" do
#user.has_password?(#attr[:password]).should be_true
end
it "should be false if the passwords don't match" do
#user.has_password?("invalid").should be_false
end
end
describe "password validations" do
it "should require a password" do
User.new(#attr.merge(:password => "", :password_confirmation => "")).
should_not be_valid
end
it "should require a matching password confirmation" do
User.new(#attr.merge(:password_confirmation => "invalid")).
should_not be_valid
end
it "should reject short passwords" do
short = "a" * 5
hash = #attr.merge(:password => short, :password_confirmation => short)
User.new(hash).should_not be_valid
end
it "should reject long passwords" do
short = "a" * 5
hash = #attr.merge(:password => short, :password_confirmation => short)
User.new(hash).should_not be_valid
end
it "should require a name" do
no_name_user = User.new(#attr.merge(:name => ""))
no_name_user.should_not be_valid
end
it "should require an email address" do
no_email_user = User.new(#attr.merge(:email => ""))
no_email_user.should_not be_valid
end
it "should accept valid email addresses" do
addresses = %w[user#foo.com THE_USER#foo.bar.org first.last#foo.jp]
addresses.each do |address|
valid_email_user = User.new(#attr.merge(:email => address))
valid_email_user.should be_valid
end
#end
it "should reject invalid email addresses" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.]
addresses.each do |address|
invalid_email_user = User.new(#attr.merge(:email => address))
invalid_email_user.should_not be_valid
end
#end
it "should reject duplicate email addresses" do
# Put a user with given email address into the database.
User.create!(#attr)
user_with_duplicate_email = User.new(#attr)
user_with_duplicate_email.should_not be_valid
end
it "should reject email addresses identical up to case" do
upcased_email = #attr[:email].upcase
User.create!(#attr.merge(:email => upcased_email))
user_with_duplicate_email = User.new(#attr)
user_with_duplicate_email.should_not be_valid
end
it "should reject names that are too long" do
long_name = "a" * 51
long_name_user = User.new(#attr.merge(:name => long_name))
long_name_user.should_not be_valid
end
end
end
end
end
My user.rb file is fine I think.
So the 3 failures thing is one aspect of my problem, but the thing that really worries me is the following command:
bundle exec rspec spec/models/user_spec.rb -e "has_password\? method"
The result in terminal is this:
No examples matched {:full_description=>/(?-mix:has_password\\?\ method)/}.
Finished in 0.00003 seconds
0 examples, 0 failures
According to Hartl I should have 2 examples, and 0 failures. Ideas? Any input appreciated :)
in your user_spec.rb file.
make sure you have:
before(:each) do
#user = User.create!(#attr)
end
right after the following line:
describe "has_password? method" do
it's missing from the code in the tutorial. you'll see it's part of the password encryption block. it looks like it stubs out a user for that test. it's not very DRY...probably a way to have that stub block run for each describe block, but that's a bit further along than i am. :) hope it helps...got my tests working.

Resources