When I am trying to run test case for model using Rspec and Factory girl it is showing uninitialized constant Wing
code in factories/wing.rb
FactoryGirl.define do
factory :wing do
wing_name "Example Title"
is_deleted "0"
mg_school_id "1"
created_by "2013-06-02 02:28:12"
updated_by "2013-06-02 02:28:12"
end
end
code in model/mg_wing_spec.rb
require 'rails_helper'
RSpec.describe MgWing, type: :model do
it "has a valid factory" do
#hai=FactoryGirl.create(:wing)
end
end
Change filename factories/wing.rb to factories/mg_wing.rb
Your factory name should be the same in your model and rspec filename.
Here are a couple of tutorials if you want it:
https://medium.com/#JonoYeong/setting-up-rspec-and-factory-girl-8cf287801099
https://semaphoreci.com/community/tutorials/working-effectively-with-data-factories-using-factorygirl
You either need the factory name same as the model name or pass the class param and factorygirl will take care of it
Solution 1
FactoryGirl.define do
factory :mg_wing do
...
end
end
Solution 2
FactoryGirl.define do
factory :wing, class: MgWing do
...
end
end
Related
I have a simple Poro, like so:
class Student
attr_reader :first_name, :last_name
def initialize(data)
#first_name = data[:first_name]
#last_name = data[:last_name]
end
end
A factory like so:
FactoryBot.define do
factory :student do
first_name {"test first name"}
last_name {"test last name"}
# https://thoughtbot.com/blog/tips-for-using-factory-girl-without-an-orm
initialize_with { new(attributes) }
end
end
A test like so:
describe 'StudentSpec', type: :model do
let(:student) {build(:student)}
context 'attributes' do
it 'respond' do
expect(student).to respond_to(:first_name, :last_name)
end
end
end
But this results in NoMethodError: undefined method 'build' for ....
Based on https://thoughtbot.com/blog/tips-for-using-factory-girl-without-an-orm, it sounds like this should work. Wondering what I am doing wrong?
Maybe you are missing require 'rails_helper' at the top of the spec file?
Also did you try to add FactoryBot?
let(:student) { FactoryBot.build(:student) }
So this is my first time writing unit tests, and Im incorporating Rspec w/FactoryBot.
My Specs were working just fine with using # instance variables, However when I use let! the second model fails because the first model was never created.
Spec:
require "rails_helper"
RSpec.describe Note, :type => :model do
before(:all) do
let!(:event){ FactoryBot.create(:event) }
let!(:note){ FactoryBot.create(:note) }
end
it "is valid with valid attributes" do
expect(note).to be_valid
end
end
Factories:
FactoryBot.define do
factory :note do
event_id Event.first.id
content "This is a sample note"
end
end
FactoryBot.define do
factory :event do
title "Event Factory Test"
event_date Date.today
event_time "1:30 PM"
end
end
As you can see, the note requires a Event id (Which requires the event to be created), but it complains when trying to find Event.first.id which should have been created from the let!).
Any ideas? This "seems" to be similar to how others use let in other rspec tests.
let and let! do not work if you wrap them in a before block.
require "rails_helper"
RSpec.describe Note, :type => :model do
let!(:event){ FactoryBot.create(:event) }
let!(:note){ FactoryBot.create(:note) }
it "is valid with valid attributes" do
expect(note).to be_valid
end
end
Also to setup an association within a factory simply pass the name of the factory:
FactoryBot.define do
factory :note do
event # short for association :event
content "This is a sample note"
end
end
(If the factory name is the same as the association name, the factory name can be left out.).
You're still thinking about factories wrong though. They should be factories that produce unique testable records. Not a set of fixtures. The way you have defined it the factory would only work if a event has been created. Never hardwire factories!
If you want to get the event later just do:
require "rails_helper"
RSpec.describe Note, :type => :model do
let!(:note){ FactoryBot.create(:note) }
it "has an event" do
expect(note.event).to be_a Event
end
end
I am learning how to test on rails from this tutorial.
On one part of the tutorial, it shows how to write invalid_attribute test:
require 'rails_helper'
RSpec.describe ContactsController, type: :controller do
describe "POST #create" do
context "with valid attributes" do
it "create new contact" do
post :create, contact: attributes_for(:contact)
expect(Contact.count).to eq(1)
end
end
context "with invalid attributes" do
it "does not create new contact" do
post :create, contact: attributes_for(:invalid_contact)
expect(Contact.count).to eq(0)
end
end
end
end
I don't understand where :contact and :invalid_contact point to.
Does :contact points to Contact class? It seems like it from FactoryGirl's gh. If so, then how can I create :invalid_contact since there is no :invalid_contact class?
I have tried post :create, contact: attributes_for(:contact, :full_name => nil) but it still fails.
spec/factories/contacts.rb:
FactoryGirl.define do
factory :contact do
full_name { Faker::Name.name }
email { Faker::Internet.email }
phone_number { Faker::PhoneNumber.phone_number }
address { Faker::Address.street_address }
end
end
First test, with valid attributes pass. On model, there is presence validation validates_presence_of :full_name, :email, :phone_number, :address. What do I add in order to pass "with invalid attributes" test?
The factory will use the class with the same name. So your :contact factory will use the Contact class. You can create a new factory for the invalid contact by specifying the class to use.
factory :invalid_contact, class: Contact do
full_name nil
end
It's also possible to use traits to avoid having two different factories.
FactoryGirl.define do
factory :contact do
full_name { Faker::Name.name }
email { Faker::Internet.email }
phone_number { Faker::PhoneNumber.phone_number }
address { Faker::Address.street_address }
trait :invalid do
full_name nil
end
end
end
Then use it with attributes_for(:contact, :invalid)
The tutorial you link to says:
Following the spec above, write a spec that uses invalid attributes to
create a new contact. This spec should check that the contact is not
created.
So you need to figure out how to test for :invalid_contact using the example for :contact.
You can just add a let in your spec:
Use let to define a memoized helper method. The value will be cached
across multiple calls in the same example but not across examples.
Source: https://www.relishapp.com/rspec/rspec-core/v/3-5/docs/helper-methods/let-and-let
Then your controller spec would look like this:
...
let(:invalid_contact) { create(:contact, name: nil) }
context "with invalid attributes" do
it "does not create new contact" do
post :create, contact: attributes_for(invalid_contact)
expect(Contact.count).to eq(0)
end
end
...
this way #post action params are picked up from invalid_contact
or as #fanta suggested in comments, you can add a trait to your factory. I prefer my method because other people looking at your code will know why invalid_contact should be invalid without looking at the :contacts factory
I am learning to do tests using Rails testing tools, mainly: rspec, factory_girl, shoulda, and faker gems.
I want to test has_one and belongs_to associations. I have user.rb (with name) and address.rb (with street, city, and user_id) models.
I am trying to figure out how to create fake associations using fakers. I want to create a fake street and associate it with a fake user, but I can't figure out how to do it on spec/factories/addresses.rb
This is what I currently have:
FactoryGirl.define do
factory :address do
street {Faker::Address.street_address}
city {Faker::Address.city}
#Creates fake user and use that user's id for user_id)#
user = build(:user, name: "Joe", id: 2)
user_id {user.id}
end
end
This is the spec/models/address_spec.rb
require 'rails_helper'
RSpec.describe Address, type: :model do
it "has a valid factory" do
address = build(:address)
expect(address).to be_valid
end
it {should belong_to(:user)}
end
When I run it, it shows add_attribute': wrong number of arguments (given 3, expected 1..2) (ArgumentError) error. I tried removing the id on the argument and just did user = build(:user, name: "Joe") but the same error message still shows.
The test passes if I don't include the user lines on addresses factory. I confirmed that address has belongs_to :user and user has has_one :address.
Is it possible to generate fake association and associate address to a fake user.id? I would like to test Address's user_id as well.
Edit - following are the related factories and models:
Factories
addresses.rb
FactoryGirl.define do
factory :address do
street {Faker::Address.street_address}
city {Faker::Address.city}
user
end
end
users.rb
FactoryGirl.define do
factory :user do
name {Faker::Name.name}
end
end
spec/models
address_spec.rb
require 'rails_helper'
RSpec.describe Address, type: :model do
it "has a valid factory" do
association user
specific_user = build(:user, name: 'Joe')
address = build(:address, specific_user)
expect(address).to be_valid
end
it {should belong_to(:user)}
end
Error running address_spec.rb
NameError:
undefined local variable or method `user' for #<RSpec::ExampleGroups::Address:0x00000001a94b00>
# ./spec/models/addres
I think the way you build association is incorrect. See https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations.
If you want a more specific associated user eg name: Joe, id: 2, it's probably better to do it in spec.
Factory:
FactoryGirl.define do
factory :address do
street {Faker::Address.street_address}
city {Faker::Address.city}
user
end
end
Spec:
it "has a valid factory" do
specific_user = build(:user, name: 'Joe')
address = build(:address, specific_user)
expect(address).to be_valid
end
I am trying to build an rspec test for a method on an sti subclass and the test only reads the parent model's method. The method works in the app, just not in the rspec test. I can't figure out what I'm missing
models/animals/animal.rb
class Animal < ActiveRecord::Base
def favorite
"unicorn"
end
end
models/animals/mammal_animal.rb
class MammalAnimal < Animal
def favorite
"whale"
end
end
models/animals/cat_mammal_animal.rb
class CatMammalAnimal < MammalAnimal
def favorite
"tabby"
end
end
mammal_animal_spec.rb
require 'rails_helper'
RSpec.describe MammalAnimal, type: :model do
let(:cat_mammal_animal) {FactoryGirl.create(:cat_factory)}
subject(:model) { cat_mammal_animal }
let(:described_class){"MammalAnimal"}
describe "a Cat" do
it "should initialize successfully as an instance of the described class" do
expect(subject).to be_a_kind_of described_class
end
it "should have attribute type" do
expect(subject).to have_attribute :type
end
it "has a valid factory" do
expect(cat_mammal_animal).to be_valid
end
describe ".favorite " do
it 'shows the favorite Cat' do
expect(cat_mammal_animal.type).to eq("CatMammalAnimal")
expect(cat_mammal_animal.favorite).to include("tabby")
expect(cat_mammal_animal.favorite).not_to include("whale")
expect(cat_mammal_animal.favorite).not_to include("unicorn")
print cat_mammal_animal.favorite
end
end
end
end
error
Failures:
1) MammalAnimal.favorite and .favorite shows the favorite Cat
Failure/Error: expect(cat_mammal_animal.type).to include("tabby")
expected "unicorn" to include "tabby"
# ./spec/models/mammal_animal_spec.rb:82:in `block (3 levels) in <top (required)>'
UPDATE
animals.rb
FactoryGirl.define do
factory :animal do
type 'Animal'
name "dragon"
trait :mammal do
type 'MammalAnimal'
name "zebra"
end
trait :cat do
type 'CatMammalAnimal'
name "calico"
end
factory :mammal_factory, traits: [:mammal]
factory :cat_factory, traits: [:cat]
end
end
as per a suggestion, I added the below line to the test
expect(cat_mammal_animal.class.constantize).to eq(CatMammalAnimal)
and got this error
1) MammalAnimal.favorite and .favorite shows the favorite Cat
Failure/Error: expect(cat_animal_mammal.class.constantize).to eq(CatMammalAnimal)
NoMethodError:
undefined method `constantize' for #<Class:0x007f8ed4b8b0e0>
Did you mean? constants
I think instead of using trait to create objects of subclasses, you should have separate factories for those too.
factory :animal do
name 'dragon'
end
factory :mammal, class: MammalAnimal do
name 'zebra'
end
factory :cat, class: CatMammalAnimal do
name 'calico'
end
All of these can be defined in animals.rb
Then you can create your objects like
create(:animal)
create(:mammal)
create(:cat)