I worked through all of Michael Hartl's Ruby on Rails Tutorial with all the tests passing. Now that I'm going back and making changes to the site to suit my own needs, it's not as cut and dry as "the tests in this section aren't passing." I've created a new "Charity" object that is strongly based on Hartl's "Micropost" object. The only difference is that instead of having "content" the object has a :name, :description and :summary.
This is the code for the test that is failing, (specifically "it { should be_valid }") which is located in /charity_spec.rb:
require 'spec_helper'
describe Charity do
let(:user) { FactoryGirl.create(:user) }
before { #charity = user.charities.build(summary: "Lorem ipsum") }
subject { #charity }
it { should respond_to(:name) }
it { should respond_to(:user_id) }
it { should respond_to(:summary) }
it { should respond_to(:description) }
it { should respond_to(:user) }
its(:user) { should == user }
it { should be_valid }
...
The test actually passes at first, but once I add the validations to the charity.rb file, they return;
Failures:
1) Charity
Failure/Error: it { should be_valid }
expected valid? to return, true, got false
...
Here's the charity.rb:
class Charity < ActiveRecord::Base
attr_accessible :name, :description, :summary
belongs_to :user
validates :name, presence: true, length: { maximum: 40 }
validates :summary, presence: true
validates :description, presence: true
validates :user_id, presence: true
default_scope order: 'charities.created_at DESC'
end
I'm sure it's something stupid, but my understanding of everything is so weak I can't figure out what I'm doing wrong, my feeling is that it's something wrong with my factory, but I really don't know.
Here's my charity factory located in the factories.rb:
factory :charity do
name "Lorem ipsum"
summary "Lorem ipsum"
description "Lorem ipsum"
user
end
When I remove the :name, :summary, and :description validations from charity.rb, the test passes. For good measure, here's the beginning of my user.rb:
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
has_many :charities
has_many :microposts, dependent: :destroy
Use your factory to have a proper charity:
before { #charity = user.charities.build(FactoryGirl.attributes_for(:charity)) }
It failed because you validate presence of attributes which were not set like name
If you need more background on FactoryGirl, their documentation is really good.
Related
I have a the following has_many model associations in my app:
User < Company < Deed < Subtransaction,
where Deed accepts_nested_attributes_for :subtransactions. I wish to test my Model validations using Minitest and fixtures.
I have trouble, however, testing the nested attributes for validity. For example , If I clear all nested attributes using
#deed.subtransactions.clear I correctly get a test response that the model is not valid.
However
#deed.subtransactions.first.num_shares = " " does not seem to work.
How do I properly test these nested attributes for validity?
My test:
class DeedTest < ActiveSupport::TestCase
def setup
#user = users(:dagobert)
#newco = companies(:newco)
params = { :deed =>
{
:date => deeds(:inc_new).date,
:subtransactions_attributes =>
{ '1' =>
{
:shareholder_name => "Shareholder 1",
:num_shares => subtransactions(:new_subt1).num_shares
},
'2' =>
{
:shareholder_name => "Shareholder 2",
:num_shares => subtransactions(:new_subt2).num_shares
}
}
}
}
#deed = #newco.deeds.new(params[:deed])
end
# test: does not raise any error messages
test "num_shares should be present for a subtransaction" do
#deed.subtransactions.first.num_shares = nil
assert_not #deed.valid?
end
# test passing: The params are submitted correctly and pass validation
test "fixture values should be valid" do
assert #deed.valid?
end
# test passing: I can test the validity of deed attributes
test "date should be present" do
#deed.date = " "
assert_not #deed.valid?
end
# test passing: when I clear #deed.subtransactions the object is no longer valid
test "a subtransaction should be present" do
#deed.subtransactions.clear
assert_not #deed.valid?
end
end
UPDATE
Deed model:
class Deed < ActiveRecord::Base
belongs_to :company
has_many :subtransactions, dependent: :destroy
accepts_nested_attributes_for :subtransactions, allow_destroy: true,
reject_if: ->(a) { a['shareholder_name'].blank? && a['num_shares'].blank? }
validates_associated :subtransactions
validates :date, presence: true
validates :subtransactions, presence: true
end
Subtransaction model:
class Subtransaction < ActiveRecord::Base
belongs_to :deed
belongs_to :shareholder
validates :num_shares, presence: true, length: { maximum: 50 },
:numericality => { only_integer: true }
validates :shareholder_name, presence: true
# end class
end
I would like to test my models but all informations that I could find seems to be outdated. My goal is to test each individual validation.
My model:
class Author < ActiveRecord::Base
has_and_belongs_to_many :books
before_save :capitalize_names
validates :name, :surname, presence: true, length: { minimum: 3 },
format: { with: /[a-zA-Z]/ }
private
def capitalize_names
self.name.capitalize!
self.surname.capitalize!
end
end
and my factorygirl define:
FactoryGirl.define do
factory :author do |f|
f.name { Faker::Name.first_name }
f.surname { Faker::Name.last_name }
end
end
So now, I want to test whether name is not shorter than 3 characters.
My context:
context 'when first name is too short' do
it { expect( FactoryGirl.build(:author, name: 'Le')).to
be_falsey }
end
I know it's invalid because of [FactoryGirl.build(:author, name: 'Le')] returns hash instead of boolean value. So now, how should I test it? What matcher should I use?
[SOLVED]
Use be_valid instead of be_falsey. Now it should look like :
context 'when first name is too short' do
it { expect( FactoryGirl.build(:author, name: 'Le')).not_to
be_valid }
end
Ok, i am trying to use Fabricator with my Rspec tests to mock some data for the tests. I'm having some trouble with a belongs_to association, however. Here's what i have so far:
user.rb
class User < ActiveRecord::Base
authenticates_with_sorcery!
belongs_to :organization
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates_presence_of :full_name
validates_presence_of :email
validates_uniqueness_of :email, on: :create
validates_format_of :email, with: VALID_EMAIL_REGEX, on: :create
validates_presence_of :password, on: :create
validates_confirmation_of :password
end
organization.rb
class Organization < ActiveRecord::Base
authenticates_with_sorcery!
has_many :users, dependent: :destroy
accepts_nested_attributes_for :users, :allow_destroy => true
validates_presence_of :name
end
integration_spec.rb
require 'rails_helper'
describe "Shopping Cart Requests" do
let!(:user) { Fabricate(:user) }
before(:each) do
login_user_post("admin#example.com", "password")
end
context "when I visit the shopping cart" do
it " show the logged in users' cart items " do
#Test stuff
end
end
end
user_fabricator.rb
Fabricator(:user) do
organization { Fabricate(:organization) }
email { "admin#example.com" }
password { "password" }
full_name { Faker::Name.name }
is_admin { true }
salt { "asdfghjkl123456789" }
crypted_password { Sorcery::CryptoProviders::BCrypt.encrypt("secret", "asdasdastr4325234324sdfds") }
activation_state { 'active' }
end
organization_fabricator.rb
Fabricator(:organization) do
name { Faker::Company.name }
website { Faker::Internet.url }
description { Faker::Lorem.paragraph }
access_code { Faker::Internet.password(10, 20) }
end
Here's the error i am getting when running the test:
Failure/Error: let!(:user) { Fabricate(:user) }
NoMethodError:
undefined method `crypted_password' for #<Organization:0x007f80ee0a44e0>
# ./spec/features/integration_spec.rb:4:in `block (2 levels) in <top (required)>'
You have authenticates_with_sorcery! in your Organization app.
If you don't intend to authenticate Organization, you should remove that line.
Cheers
I have been creating an application based on the Hartl course, and have added in the concept of Organization, which has_many users. The tests are all the standard tests Hartl recommends up to section 9.2 of the guide book. Since implementing Organizations into the application, one of the test cases is failing "when email address is already taken" - this should block a user from signing up with the same email address twice. What is odd is the fact that this is working in the application itself (form error - "users email address is already taken" thrown) but not in my tests. Can you help and indicate why this has broken please?
User code:
class User < ActiveRecord::Base
belongs_to :organization
#accepts_nested_attributes_for :organization
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
validates :organization, presence: true
Organization code:
class Organization < ActiveRecord::Base
validates :organization_name, presence: true, length: { maximum: 50 }, uniqueness: true
has_many :users, :inverse_of => :organization
accepts_nested_attributes_for :users
User spec:
require 'spec_helper'
describe User do
before do
#user = FactoryGirl.create(:user)
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:remember_token) }
it { should respond_to(:authenticate) }
it { should be_valid }
...
describe "when email address is already taken" do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
Factory Girl Code:
FactoryGirl.define do
factory :organization do
organization_name "Example Org"
trait :wrong do
organization_name "Wrong Org"
end
trait :also_wrong do
organization_name "Another Wrong Org"
end
end
factory :user do
association :organization
name "Example Name"
email "email#example.com"
password "foobar"
password_confirmation "foobar"
trait :wrong_org do
association :organization, :factory => [:organization, :wrong]
end
trait :wrong_org2 do
association :organization, :factory => [:organization, :also_wrong]
end
end
end
The error thrown from the Rails console is as follows:
1) User when email address is already taken should not be valid
Failure/Error: it { should_not be_valid }
expected #<User id: 5287, name: "Example Name", email: "email#example.com", created_at: "2014-07-22 15:04:33", updated_at: "2014-07-22 15:04:33", password_digest: "$2a$04$jrxyuz9e574BoaAhZm6xkOUeAY5spyDut2CCEvAykMu...", organization_id: 5025, remember_token: "339dfafcac7bc5925dbf4e44f60a782f3bbbaa1b">.valid? to return false, got true
I've tried changing the code inside the test, but no matter what I do it keeps throwing an error. As mentioned above, when I open up the application in my local server I can use all functions, and when I try to sign up using a duplicate email address it won't let me. What's wrong with my test code?
The #user is completely valid:
You create your subject, #user. This is valid
You create the user_with_same_email
That user is not valid, because it has the same email as #user
Saving user_with_same_email returns false, but that is not checked in your test
The duplicate user is not saved in the db
The original user is still valid
A correct test would just #dupthe user (or make a new one with the same email), and then check that the new record is not valid.
My associations aren't so complex but I've hit a wall making them work with FactoryGirl:
Text: blast_id:integer recipient_id:integer
class Text < ActiveRecord::Base
belongs_to :blast
belongs_to :recipient, class_name: "User"
validates :blast_id, presence: true
validates :recipient_id, presence: true
end
Blast: content:string author_id:integer
class Blast < ActiveRecord::Base
belongs_to :author, class_name: "User"
has_many :texts
validates :author_id, presence: true
end
User: name:string, etc. etc.
class User < ActiveRecord::Base
has_many :blasts, foreign_key: "author_id"
validates :name, presence: true
end
In FactoryGirl I've got:
FactoryGirl.define do
factory :user, aliases: [:author, :recipient] do |u|
sequence(:name) { Faker::Name.first_name }
end
factory :blast do
author
content "Lorem ipsum"
ignore do
texts_count 1
end
after :build do |blast, evaluator|
blast.texts << FactoryGirl.build_list(:text, evaluator.texts_count, blast: nil, recipient: FactoryGirl.create(:user) )
end
end
factory :text do
blast
association :recipient, factory: :user
end
end
Finally, some specs which all fail because Texts is not valid
require 'spec_helper'
describe Text do
User.destroy_all
Blast.destroy_all
Text.destroy_all
let!(:user) { FactoryGirl.create(:user) }
let!(:blast) { FactoryGirl.create(:blast, author: user) }
let(:text) { blast.texts.first }
subject { text }
it { should be_valid }
describe "attributes" do
it { should respond_to(:blast) }
it { should respond_to(:recipient) }
its(:blast) { should == blast }
its(:recipient) { should == recipient }
end
describe "when blast_id is not present" do
before { text.blast_id = nil }
it { should_not be_valid }
end
describe "when recipient_id is not present" do
before { text.recipient_id = nil }
it { should_not be_valid }
end
end
All the specs fail on FactoryGirl blast creation with:
1) Text
Failure/Error: let!(:blast) { FactoryGirl.create(:blast, author: user) }
ActiveRecord::RecordInvalid:
Validation failed: Texts is invalid
# ./spec/models/text_spec.rb:8:in `block (2 levels) in <top (required)>'
I've tried various iterations of the association code in the FactoryGirl docs and other question answers like this one but my situation is different enough that I can't get it to work.
If you've made it this far, thank you! Super grateful for any leads.
Your factory for "blast" should look like
factory :blast do
author
content "Lorem ipsum"
ignore do
texts_count 1
end
after :build do |blast, evaluator|
blast.texts << FactoryGirl.build_list(:text, evaluator.texts_count, blast: blast, recipient: FactoryGirl.create(:user) )
end
end
In other words, you immediately create the correct "parent" by connecting the newly created blast to the newly created tekst
To further dry your code, have a look at https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#configure-your-test-suite, describing how to get rid of using "FactoryGirl." over and over again by setting
config.include FactoryGirl::Syntax::Methods
once in your settings