Rails 3: Duplicate validation error messages during testing - ruby-on-rails

I'm getting some weird validation behavior: it's duplicating my validation error messages and I can't figure out what's causing it... it doesn't do this in the rails console.
Here is the validation from my Phone model:
# phone.rb
validates :number, :length => { :minimum => 3 }
My spec:
require 'spec_helper'
describe Phone do
it "requires a number" do
user = User.make!
#p = Phone.new(number:nil,user_id:user.id,type:2)
#p.valid?
puts #p.errors.inspect
#p.should have(1).error_on(:number)
end
My test results:
# rspec and machinist
#<ActiveModel::Errors:0x000000036f1258 #base=#<Phone id: nil, user_id: 614, kind: nil, number: nil, created_at: nil, updated_at: nil>, #messages={:number=>["is too short (minimum is 3 characters)", "is too short (minimum is 3 characters)"]}>
F
Failures:
1) Phone requires a number
Failure/Error: #p.should have(1).error_on(:number)
expected 1 error on :number, got 2
# ./spec/models/phone_spec.rb:11:in `block (2 levels) in <top (required)>'
Finished in 0.50988 seconds
1 example, 1 failure
As you can see, I'm getting "is too short (minimum is 3 characters)" twice... It's also /only/ happening during testing.
Any ideas?
Thanks!

The problem is in the line:
Dir["#{Rails.root}/app/**/*.rb"].each { |f| load f }
in the spec_helper.rb file, in the Spork.each_run block
If you change the method 'load' to 'require', it fixes the problem.
Or if you have a recent enough version of Spork, you can remove the line altogether.
I am pretty sure the error is caused when someone is installing a newer version Spork(0.9.0+) with old instructions, because the line:
Dir["#{Rails.root}/app/**/*.rb"].each { |f| load f }
doesn't even has to be stated explicitly in the spec_helper.rb file anymore. If it is then when the load method is used in the spec_helper.rb file, it reloads the files specified , most likely the cause of the strange RSpec duplicate validations errors.

I encountered a similar issue of duplicate error messages, but it seemed to stem from my use of a different directory structure than the standard, e.g.:
- app
\- models_one
|- models_two
|- models_three
My load/require call in the Spork.each_run block looked like this:
Dir["#{Rails.root}/app/models_*/*.rb"].each { |f| load f }
I removed this and replaced it with these:
ActiveSupport::Dependencies.clear
ActiveRecord::Base.instantiate_observers
And there were no more duplicate error messages.
I was helped by this post: http://adams.co.tt/blog/2012/04/12/duplicate-active-model-validation-errors/ in which the author says it's a 1.8.7-specific problem which involves requiring different paths which resolve to the same file, but I am using 1.9.3 so it may not be related.

I'm not sure if this is going to solve your problem, but rspec is very quirky if you don't follow rspec convention. Try some more idiomatic rspec like the following:
require 'spec_helper'
describe Phone do
context :validations do
let(:user) { double(:user) }
subject { Phone.new(number:nil,user_id:user.id,type:2) }
before do
subject.valid?
end
it 'should have a minimum length of 3' do
subject.should have(1).error_on(:number)
end
end
end
I'd also like to suggest that you shouldn't unit test Rails' built in validations; they are already tested in Rails itself. Specifically, your length validation is tested in activemodel/test/cases/validations/length_validation_test.rb. The behavior of the validations should be covered in your integration tests.
I hope that's helpful.

I'm having the same problem (using rspec & spork).
I suspect it's something to do with the model being required twice, causing validations to run twice.
If you explicity require 'phone' at the top of your spec, it seems to fix it.
But I'd really like to know what's causing the problem...

Related

Upgrade to rspec 3 cause error when using should have(1).error_on

Since I updated my Gemfile and moved to rspec 3, in many tests, I'm getting a error for: way:
it "should reject attribute that are too short" do
short = "a" * 3
hash = #attr.merge(:details => short)
Deal.new(hash).should have(1).error_on(:details)
end
I'm getting this error:
Failure/Error: Deal.new(hash).should have(1).error_on(:details)
NoMethodError:
undefined method `have' for #<RSpec::ExampleGroups::Deal_2::TestsOnDealsModelsValidations>
I read I should now be using "expect" instead of should but here with have(1).error_on, how should I write it to comply with rspec 3?
I tried the following but it still does not work:
it "should reject attribute that are too short" do
short = "a" * 3
hash = #attr.merge(:details => short)
expect(Deal.new(hash).error_on(:details).size).to eq(1)
end
I have replaced the likes of
Deal.new(hash).should have(1).error_on(:details)
with
deal = Deal.new(hash)
expect(deal.valid?).to be_falsey
expect(deal.errors[:details].size).to eq(1)
The first expectation with valid? is necessary as it initializes the errors list.
have and other similar matchers have been moved out of rspec core and into another gem, rspec-collection-matchers.
I recommend following the upgrade path from rspec 2 -> 3 as detailed in the rspec docs: https://relishapp.com/rspec/docs/upgrade
Upgrade to rspec 2.99
Run your test suite
Fix deprecation warnings
Upgrade to rspec 3.
If you had done this you would have received a deprecation error with your code that would have also told you what to do to fix it.
The line to add to your Gemfile should be:
gem 'rspec-collection_matchers'

FakeFS and Rspec incosistency with Rspec and real filesystem

I'm trying to write some tests involving file operations. I want to use some fake file system (something like VCR for external services) and I have found fakeFS. Unfortunately, either I can't set it right or something is broken (which I doubt, it's quite basic function), I've prepared simple example which illustrates what I mean, let the code speak:
With real FS:
module MyModule
describe Something do
before(:all) do
File.open("#{Rails.root}/foo.txt", 'w+') { |f| f.write 'content'}
end
it 'should exist' do
expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
end
it 'should still exist' do
expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
end
end
end
Running that gives:
bash-4.2$ rspec
..
Finished in 0.00161 seconds
2 examples, 0 failures
Adding fakeFS in such way:
require 'fakefs/spec_helpers'
module MyModule
describe Something do
include FakeFS::SpecHelpers
FakeFS.activate!
FakeFS::FileSystem.clone(Rails.root)
before(:all) do
File.open("#{Rails.root}/foo.txt", 'w+') { |f| f.write 'content'}
end
it 'should exist' do
expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
end
it 'should still exist' do
expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
end
end
end
results in:
bash-4.2$ rspec
.F
Failures:
1) MyModule::Something should still exist
Failure/Error: expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
expected: true value
got: false
# ./spec/models/something_spec.rb:23:in `block (2 levels) in <module:MyModule>'
Finished in 0.00354 seconds
2 examples, 1 failure
So it seems like file is not persisted through subsequent tests. Do I misunderstand how before(:all) works or do I do something wrong? If so then why that code works with real files?
If it is 'not a bug, just a feature' then is there any other fake filesystem gem which is consistent with real one? Or do I have to stay with real files to get tests that.. well, test?
I found the answer just after creating that question, duh ;) I've looked into source of that lib and found suspicious line.
Instead of FakeFS::SpecHelpers I've included FakeFS::SpecHelpers::All which is the same code except FakeFS::FileSystem is not being cleared after each call, now it behaves correctly.

`marshal data too short` error when executing a capybara test using poltergeist a few times

When I've executed a decent capybara test using poltergeist as a driver like below a few times,
require 'spec_helper'
describe 'Capybara test', js: true do
it 'works' do
visit '/'
end
end
I start to get an error "Marshal data too short" when reading the compiled JS file(?).
The backtrace is something like below.
1) Capybara test works
Failure/Error: Unable to find matching line from backtrace
ActionView::Template::Error:
marshal data too short
# ./app/views/layouts/_javascripts.html.erb:7:in `_app_views_layouts__javascripts_html_erb__4483406095691079466_70112937186160'
# ./app/views/layouts/application.html.erb:5:in `_app_views_layouts_application_html_erb__419483610520628071_70112936724940'
# ./app/controllers/home_controller.rb:29:in `index'
./app/views/layouts/_javascripts.html.erb:7 is
<%= javascript_include_tag 'application' %>
and ./app/views/layouts/application.html.erb:5 is
<%= render 'layouts/javascripts' %>
I've seen some couple questions asking why this happen when creating a AR object(which the actual reason is too much session data), but I think this is a different reason since it's happening when reading a JS file.
Any guess?
ref: marshal data too short
rake tmp:clear solved the question.
Since there were no files under tmp/sessions, I didn't try out this command, but seems that the cache was triggering this problem.

FactoryGirls randomly fails with 'Factory not registered', why?

I've some tests that randomly fail, approx. 20% of times. It means that WITHOUT changing the code, each time that I run the tests 1 time out of 5 will fail with "Factory not registered" error. It's very weird.. :(
This is the consone output:
Failures:
1) Unit#new_from_string returns factor for metric conversions
Failure/Error: FactoryGirl.create :taza
ArgumentError:
Factory not registered: taza
# ./spec/models/unit_spec.rb:29:in `block (2 levels) in <top (required)>'
Finished in 0.29619 seconds
4 examples, 1 failure
Failed examples:
rspec ./spec/models/unit_spec.rb:22 # Unit#new_from_string returns factor for metric conversions
Randomized with seed 61727
And this is the code:
file: "unit_spec.rb"
require 'spec_helper'
describe Unit, "#new_from_string" do
it "parses the string and returns a Unit object" do
[some tests...]
FactoryGirl.find_definitions
u = FactoryGirl.create :taza
FactoryGirl.create :tbsp
[tests...]
end
it "returns factor for metric conversions" do
[tests not involving factory girl...]
# the following is line 29, that fails
FactoryGirl.create :taza
[tests...]
end
end
file "spec/factories/units.rb":
FactoryGirl.define do
factory :taza , :class => CustomUnit do
singular 'taza'
plural 'tazas'
physical_type Unit::VOLUME
equivalence_factor 200
equivalence_unit 'ml'
end
[other factories...]
end
I think the problem is on this line
FactoryGirl.find_definitions
Actually there is no need for this line when your factories are in correct directories(I see it is), and you put gem factory_girl_rails in Gemfile.
I think, 20% of time, the second test get run at first. At this time, there is no definition of Factory and the test failed. The other test has such definition and get passed. In other time, the first test run first so definition exists.
My suggestion:
Make sure you have factory_girl_rails, not factory_girl in Gemfile.
Remove that line of definition.
[optional but recommended] Put all definition in a single file spec/factories and remove all other factory files, if you don't have too much factories. This would be easier to manage.

Shoulda for Rspec, Rails, outputs there is an error but doesn't output the error itself

Well, I'm doing some testing right with Rails+Rspec+Shoulda.
When I do a test like the following:
context #user do
describe 'Validation' do
describe :name
it { should allow_value('something').for :name }
end
end
end
When it fails, Rspec just output:
1) Validation name Valid
Failure/Error: it { should allow_value(value).for :name }
Did not expect errors when name is set to "something", got error:
# ./spec/models/user_spec.rb:4:in `block (3 levels) in <top (required)>'
It even says got error: but it doesn't output it! I actually know there is a validation error there, but I want Rspec to tell me that, how I would know what is failing to validate then?
What am I doing wrong? Is that the expected behaviour? I have to overwrite the helpers?
I dug into the Shoulda code and I found that it doesn't show the errors when checking for positive assert. But them are loaded into the #errors variable. So I just monkey patched the one method that defines the output:
module Shoulda
module ActiveRecord
module Matchers
def failure_message
"Did not expect #{expectation}, got error: \n#{#expected_message ? #matched_error : #errors.join("\n ")}"
end
end
end
end
The original said:
"Did not expect #{expectation}, got error: #{#matched_error}"
I saved it to /lib/shoulda/activerecord/matchers.rb and loaded it with config.autoload_paths += Dir["#{config.root}/lib/**/"]
Hope this helps someone with the same issue ^^
Yup welcome to spec testing so you need to recreate the error in console if you want the error, rspec is not a debugger just a test suite.
I run into this a lot

Resources