I'm just starting to have a play with Rails (using Rspec and Shoulda Matchers) to build a demo blog.
I've literally just started and my first test is failing but I can't understand why.
I think I've set up everything correctly, but when I try to validate that a title is present on my Article model but it returns a failure
Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError:
The matcher attempted to set :title on the Article to nil, but that
attribute does not exist.
My model looks like this...
class Article < ApplicationRecord
# attr_accessor :title
validates_presence_of :title
end
and my test...
require 'rails_helper'
RSpec.describe Article do
it { should validate_presence_of :title }
end
If I uncomment out the attr_accessor then the test passes but I understand that it's not required with Rails.
What am I doing wrong?
The attr_accessor is not required, as long as your articles database table has a title column.
You can check that in db/schema.rb.
Related
When running a test, if I try to create a new object using User.new I get an error. If instead I use User.new({}), it works fine.
Isn't params supposed to be defaulted to empty if not passed in?
$ rails -v
Rails 5.0.0.1
user.rb
class User
include ActiveModel::Model
attr_accessor :name, :email, :country
end
user_test.rb
require 'test_helper'
class User < ActiveSupport::TestCase
test "should create an empty user when initialized with no params" do
user = User.new
assert_not_nil(user, 'user cannot be empty')
end
end
test result
Error:
User#test_should_create_an_empty_user_when_initialized_with_no_parameters:
ArgumentError: wrong number of arguments (given 0, expected 1)
test/models/user_test.rb:7:in `new'
test/models/user_test.rb:7:in `block in <class:User>'
Generally, attr_accessor is used on a model for columns that are not actual columns in the SQL table.
So if your model has columns :name, :email and :country, then declaring attr_accessor is unnecessary. I think rails is waiting for you to declare them now.
Try commenting out the attr_accessor :name, :email, :country line, then re-run your test.
This may help
Instead of including model class extend it like:
class Crime < ApplicationRecord
//do your stuff here
end
Then you can use #user = User.new
The User you're referencing in the test is the test itself.
The easiest solution is to follow Ruby convention and name the test class UserTest.
I followed the Restauranlty tutorial at rubyonrailstutor.com and everything worked perfectly. Now I'm trying to implement the same tests in my project, which has more models with associations between them and I'm getting errors.
I've tried researching this problem, and even came across a few questions on this site - most notably this one and this one.
I have the following models:
class Album < ActiveRecord::Base
belongs_to :artist
validates_presence_of :title
validates_presence_of :no_of_tracks
end
class Artist < ActiveRecord::Base
has_many :albums
end
And I have the following factories:
FactoryGirl.define do
factory :artist do
name "MyString"
image "MyString"
bio "MyString"
end
end
FactoryGirl.define do
factory :album do
title "MyString"
no_of_tracks 0
artist
end
end
Apart from following the Restaurantly tutorial, this is my first time testing so I also looked at this guide for setting up factory girl for models with associations.
My test is:
describe Album do
subject(:album) { FactoryGirl.build :album, title: nil, artist: nil,
no_of_tracks: nil }
it { expect(album.valid?).to be_false }
end
But when I run it it fails with the error: can't write unknown attribute artist_id.
The questions I mentioned above suggested that perhaps my database has not been properly migrated but I've run both
bundle exec rake db:migrate RAILS_ENV=test
and
bundle exec rake db:test:prepare
to no avail. As I said, I'm very, very new to testing so if anyone could point me in the right direction I'd be grateful.
Everything looks fine.
Do you really have a column artist_id in your albums table ?
I suspect that albums table in your database does not have artist_id foregin key.
I'm trying to implement shoulda unit tests on a rails 2.3.5 app using mongomapper.
So far I've:
Configured a rails app that uses mongomapper (the app works)
Added shoulda to my gems, and installed it with rake gems:install
Added config.frameworks -= [ :active_record, :active_resource ] to config/environment.rb so ActiveRecord isn't used.
My models look like this:
class Account
include MongoMapper::Document
key :name, String, :required => true
key :description, String
key :company_id, ObjectId
key :_type, String
belongs_to :company
many :operations
end
My test for that model is this one:
class AccountTest < Test::Unit::TestCase
should_belong_to :company
should_have_many :operations
should_validate_presence_of :name
end
It fails on the first should_belong_to:
./test/unit/account_test.rb:3: undefined method `should_belong_to' for AccountTest:Class (NoMethodError)
Any ideas why this doesn't work? Should I try something different from shoulda?
I must point out that this is the first time I try to use shoulda, and I'm pretty new to testing itself.
After studying shoulda more profoundly, I realized what was wrong.
Shoulda's macros (should_belong_to, should_have_many, should_validate_presence_of) are only available for ActiveRecord - after all they are defined on Shoulda::ActiveRecord::Macros.
If I were to use those, I would have to implement macros for Shoulda::MongoMapper::Macros. I'm not sure it is worth it.
I hope this helps anyone finding this post.
Everybody knows that automated testing is a good thing.
Not everybody knows exacly what to test.
My question is if native validations like validate_presence_of, validate_uniqueness_of and so on should be tested in the application.
In my office we are three, one thinks it should be tested, one thinks it shouldn´t and I am up in the air.
Yes.
Testing that a model attribute is present or not is only testing the validates_presence_of code as a by-product of the real test which is that the validates_presence_of exists within your model.
If someone commented out a bunch of validation code and then forgot to uncomment it then this would go undetected and could cause all sorts of problems.
I test them, not because I think they don't work but to ensure that they are present in my model when required.
Are you planning to write unit tests for every single Ruby operator and API method as well?
Your unit tests should test your own code, not other people's - that's their job, why duplicate their work? And if you don't trust them to do their job well, why are you using their code?
Matthew Bass has a great gem he's released for just this type of thing. It adds rspec matchers that check to make sure the validation is in place without actually running the underlying ActiveRecord code. Read more about it here.
It adds matchers for validations:
it_should_validate_presence_of :first_name, :last_name, :email
it_should_validate_numericality_of :zip
it_should_validate_uniqueness_of :email
it_should_validate_inclusion_of :gender, :in => %w(Male Female)
Also matchers for associations:
it_should_belong_to :employer
it_should_have_many :friends, :romans, :countrymen
it_should_have_one :account
it_should_have_and_belong_to_many :comments
And a few other useful additions:
# tests that User.count increases by 1
it_should_be_createable :with => {:first_name => 'Ed', :last_name => 'The Duck', :email => 'a#b.com'}
# tests that the attribute is protected
it_should_protect :email
That's not by any means an exhaustive list. I've got a fork where I've added a few others I needed, likely there are others floating around as well. It's a good approach and for me fit the middle ground between ensuring the validations were still in the model, and having to explicitly write tests to execute ActiveRecord code to ensure it.
This is where tools like Shoulda really come in handy. I think its totally up to you to test how you write the code with the tools people provide you. Just because you are using has_many, does not mean you are using it right!
Seriously, if you integrate Shoulda into the mix, it becomes trivial to test these sorts of things. if you run rcov, its going to tell you all the code you wrote is not fully tested, unless you do.
Test the code you write. ActiveRecord has great test coverage including coverage for the validation class methods.
Spec:
require 'spec_helper'
describe User do
before(:each) do
#user = User.new
end
it "should not be valid without an email" do
#user.save.should be_false
#user.should_not be_valid
#user.email = "example#example.com"
#user.should be_valid
#user.save.should be_true
end
end
To get that spec to pass you would need
class User < ActiveRecord::Base
validates_presence_of :email
end
I'm writing a small CMS as a Rails test project (also planning to use it for my personal website). I want SEO-friendly URLs so I have a test to verify that permalinks are automatically being created based on a page's title (e.g. About Us => about-us). I can't figure out why this test is failing, however. Here's the code (I'm using Rails 2.3.2):
# page_test.rb
# note I am using the "shoulda" framework
require 'test_helper'
class PageTest < ActiveSupport::TestCase
should_validate_presence_of :title, :permalink, :content
should_validate_uniqueness_of :title
should "create permalink automatically" do
p = pages(:sample_page)
p.save
assert_equal "sample-page", p.permalink
end
end
# pages.yml
sample_page:
title: Sample Page
permalink: # gets automatically created by model
content: This is a sample page
# page.rb
class Page < ActiveRecord::Base
validates_presence_of :title, :permalink, :content
validates_uniqueness_of :title
before_save :generate_permalink
private
def generate_permalink
self.permalink = self.title.parameterize
end
end
What happens is that the permalink is nil, instead of "sample-page" like it's supposed to be. It works, however, if I manually put the permalink in the fixture and change the test around, for example:
p - pages(:sample_page)
p.title = "Contact Us"
p.save
assert_equal "contact-us", p.permalink
I could fix it like this, but I'm wondering why it's not firing the before_save method for the original test.
Alright, I was able to figure it out. I needed to use before_validation as the callback and not before_save
Does it work if you remove the empty permalink: key from your pages.yml file?