I've noticed a couple examples in Rspec and FactoryGirl where some people put the class name in quotes, and some don't.
Example rspec:
describe "User" do
...specs...
end
describe User do
...specs...
end
Example FactoryGirl
factory :high_school_account, class: "Account" do
Name "Test Account Name"
AccountTypeId 1
end
factory :high_school_account, class: Account do
Name "Test Account Name"
AccountTypeId 1
end
I thought I read somewhere it had to do when the class is loaded into the ruby environment, but I might be completely making that up.
Is there a difference between the quoted and non-quoted versions?
From the factory_girl source code:
module FactoryGirl
class Factory
# ...
def build_class
#build_class ||= if class_name.is_a? Class
class_name
else
class_name.to_s.camelize.constantize
end
end
end
end
So no, in this context there is no difference - both are accepted. A String is simply converted to the class. Even a symbol would work.
Related
I'm trying to create a shared_example in a rails application. It's working but I'm getting a deprecation error. I can I refactor the code to fix this?
error
Looking up factories by class is deprecated and will be removed in 5.0. Use symbols instead and set FactoryBot.allow_class_lookup = false.
source
shared_examples 'a sanatized_record' do
subject { build(described_class) }
describe 'stripped_attributes' do
described_class::STRIPPED_ATTRIBUTES.each do |attr|
it "strips whitespaces from #{attr}" do
original = subject[attr]
subject[attr] = " #{original} "
subject.validate
expect(subject[attr]).to eq original
end
end
end
end
Related post on the topic: FactoryBot namespaced models without class_name
From the link -- updating the factory to
factory :foo_bar, class: 'foo/bar' do; end should work because of key.to_s.underscore.to_sym in this class
I can't figure how to test class method calls from within the class body.
How can I test it?
class User
act_as_paranoid
end
it 'is called from class body' do
expect(User).to receive(:acts_as_paranoid)
User.new
end
It's usually recommended to test the behavior, not the implementation. In this case, whatever acts_as_paranoid provides for this class in terms of behavior, is what you want to test.
However, if you trust that calling acts_as_paranoid correctly provides all the behavior you need and just want to test that it is added to the class, you can use:
assert User.included_modules.include? ActsAsParanoid::Core
To figure this out I just briefly looked at the source code for acts_as_paranoid here: https://github.com/ActsAsParanoid/acts_as_paranoid/blob/master/lib/acts_as_paranoid.rb#L8
You can see that on line 50, it extends the ActsAsParanoid module to ActiveRecord::Base, which gives the model classes access to the acts_as_paranoid method. And if you look at the definition of this method, you can see it calls include ActsAsParanoid::Core
Updated
This is not the greatest way to do this but if you must this is closer to what you want:
describe 'Check if a string method is in a file' do
it 'matches a string pattern' do
lines = File.read('user.rb').split("\n")
assert lines[1][/\b+acts_as_paranoid/]
#hacky way to make sure you don't accidentally comment it out
assert lines[1].split('#').count == 1
end
end
Original answer:
There is nothing here to test. Your class definition is invalid unless your method is defined when user.rb file loads. That is core ruby. Prove it.
#user_spec.rb
require 'minitest/autorun'
require_relative 'user'
describe 'User' do
it 'is a valid class' do
assert User
end
end
#user.rb
class User
acts_as_paranoid
end
If acts_as_paranoid is not defined before ruby loads user.rb, spec fails as soon as the file is required. If this is all the code you have this test fails. Comment out acts_as_paranoid test will pass.
To just test that you added acts_as_paranoid to User, you can do:
it 'has acts_as_paranoid' do
expect(User).to respond_to(:acts_as_paranoid)
end
MyClass.inspect return incorrect class when I run whole test suite.
Problem:
I have User::CreditCard and ActiveMerchant::Billing::CreditCard classes in project. Last from activemerchant gem.
When I run single spec(rspec spec/models/user/credit_card_spec.rb) then it works correctly.
When I run whole suite(rspec spec) then spec fails with undefined method..., it doesn't matter. The problem is that in this case, my CreditCard class is not mine!!!
When I run single spec and do puts User::CreditCard.inpsect(or just p User::CreditCard, or in pry just User::CreditCard) then it returns User::CreditCard as expected.
When I run whole suite and do p User::CreditCard inside spec then it returns ActiveMerchant::Billing::CreditCard.
Background:
If you don't want to read "background" then be sure that there are NOTE in the end
I'm working with legacy code. So I don't fully know all parts of the image.
I want to create Value Object for credit card in my User. So I've create new tableless model(note the path and class name):
#app/models/user/credit_card.rb
class User::CreditCard
include ActiveModel::Model
delegate :card_number, :card_expiration, :card_type, to: :subscription
def initialize(subscription)
#subscription = subscription || Subscription.new
end
private
attr_reader :subscription
end
Of course I have User model:
#app/models/user.rb
class User
...
has_one :subscription
...
def credit_card
#credit_card ||= User::CreditCard.new(subscription)
end
end
My specs for user/credit_card:
#spec/models/user/credit_card_spec.rb
require 'spec_helper'
# require 'user/credit_card' # if I include this then it works correct
RSpec.describe User::CreditCard, type: :model do
let(:subscription) { build :subscription }
let(:credit_card) do
p User::CreditCard # this result depends on whole/not whole suite run...
# rspec spec => ActiveMerchant::Billing::CreditCard
# rspec spec/models/user => User::CreditCard
User::CreditCard.new(subscription)
end
it 'should delegate alowed messages to user subscription' do
%w[card_number card_expiration card_type].each do |attr|
expect(credit_card.public_send(attr)).to eql subscription.public_send(attr)
end
end
it 'disallow another methods' do
expect { credit_card.unexisted_method }.to raise_error(NoMethodError)
end
end
NOTE:
in spec I can require 'user/credit_card' and then it will work. But why it does not work without it?
Can it be a problem in another places? For example in controllers or somewhere else?
This is a glitch of rails autoloading + ruby constant resolution.
class C; end
CONST = 42
C::CONST
#⇒ (pry):3: warning: toplevel constant CONST referenced by C::CONST
#⇒ 42
Surprisingly enough, CONST was resolved. That is because of Ruby constant resolution algorithm.
One has two options to fix the problem: either to give a different name to the class User::CreditCard or to make sure it’s loaded. Otherwise Rails finds the constant CreditCard in ActiveMerchant::Billing namespace and is happy with using it.
Any links to documentation proving or disproving my thoughts here would be very appreciated; I can't seem to find any.
AFAIK, if you had a Rails application with a Product model, you could define a FactoryGirl factory as
FactoryGirl.define do
factory :product do
# stuffs
end
end
and then call your factory in tests with (RSpec example)
let(:product) { FactoryGirl.create(:product) }
but you may also call it with
let(:product) { FactoryGirl.create(Product) }
This is helpful if you're wanting to keep your model tests a bit more dynamic and free to change with RSpec's described_class helper.
My problem:
I've got a model that happens to be namespaced
class Namespace::MyModel < ActiveRecord::Base
# model stuffs
end
with a factory
FactoryGirl.define do
factory :my_model, class: Namespace::MyModel do
# factory stuffs
end
end
and when attempting to use RSpec's helpers...
RSpec.describe Namespace::MyModel do
let(:my_object) { FactoryGirl.create(described_class) }
# testing stuffs
end
FactoryGirl complains of a missing factory
Factory not registered: Namespace::MyModel
Am I missing this feature of FactoryGirl, without understanding its true purpose? Or is there another way I can define my factory to resolve correctly?
Why don't you try
RSpec.describe Namespace::MyModel do
let(:my_object) { FactoryGirl.create(:my_factory) }
# testing stuffs
end
FactoryGirl is usually used by factory name, but not class name, that is defines.
You can have a multiple factories, that define instances of the same class. The difference between them can be in fields values, for example.
Or you can dynamicly get factory name, from described_class name.
It is already answered at How do you find the namespace/module name programmatically in Ruby on Rails?
I have an Active Record based model:- House
It has various attributes, but no formal_name attribute.
However it does have a method for formal_name, i.e.
def formal_name
"Formal #{self.other_model.name}"
end
How can I test that this method exists?
I have:
describe "check the name " do
#report_set = FactoryGirl.create :report_set
subject { #report_set }
its(:formal_name) { should == "this_should_fail" }
end
But I get undefined method 'formal_name' for nil:NilClass
First you probably want to make sure your factory is doing a good job creating report_set -- Maybe put factory_girl under both development and test group in your Gemfile, fire up irb to make sure that FactoryGirl.create :report_set does not return nil.
Then try
describe "#formal_name" do
let(:report_set) { FactoryGirl.create :report_set }
it 'responses to formal_name' do
report_set.should respond_to(:formal_name)
end
it 'checks the name' do
report_set.formal_name.should == 'whatever it should be'
end
end
Personally, I'm not a fan of the shortcut rspec syntax you're using. I would do it like this
describe '#formal_name' do
it 'responds to formal_name' do
report_set = FactoryGirl.create :report_set
report_set.formal_name.should == 'formal_name'
end
end
I think it's much easier to understand this way.
EDIT: Full working example with FactoryGirl 2.5 in a Rails 3.2 project. This is tested code
# model - make sure migration is run so it's in your database
class Video < ActiveRecord::Base
# virtual attribute - no table in db corresponding to this
def embed_url
'embedded'
end
end
# factory
FactoryGirl.define do
factory :video do
end
end
# rspec
require 'spec_helper'
describe Video do
describe '#embed_url' do
it 'responds' do
v = FactoryGirl.create(:video)
v.embed_url.should == 'embedded'
end
end
end
$ rspec spec/models/video_spec.rb # -> passing test