FactoryGirl Sequence not instantiating when creating an object with association - ruby

I have to write a few tests for a controller I have. I have 3 models : User, UserInfo and UserPaymentPreference. I am using FactoryGirl to make a UserPaymentPreference object like this:
factory :advertiser_payment_preference_sequence do
association :advertiser_user, factory: [:advertiser_user_sequence]
end
factory :advertiser_user_sequence, class: 'AdvertiserUser' do
deposit { rand(100..1000).round(2) }
sequence(:login) { |n| "#{Faker::Internet.domain_name}#{n}" }
user_type 'advertiser'
association :user_info, factory: :advertiser_user_info_sequence
trait :no_deposit do
deposit 0
end
end
factory :advertiser_user_info_sequence, class: 'AdvertiserUserInfo' do
sequence(:firstname) { |n| "#{Faker::Name.first_name}#{n}" }
sequence(:organization) { |n| "#{Faker::Company.name}#{n}" }
sequence(:phone) { |n| "#{Faker::PhoneNumber.cell_phone}#{n}" }
sequence(:fiscal_code) { |n| "#{Faker::Company.duns_number}#{n}" }
sequence(:address) { |n| "#{Faker::Address.street_address}#{n}" }
sequence(:city) { |n| "#{Faker::Address.city}#{n}" }
end
This works fine if I do something like FactoryGirl.build(:advertiser_user_info_sequence) it will create an object of type UserInfo with all the fields populated as they should be.
However when I try to make an AdvertiserPaymentPreference object by doing FactoryGirl.build(:advertiser_payment_preference_sequence) all the user_info fields will be "" instead of the value that it should have been created by the association.
describe 'Advertiser::Billing::SettingsController', type: :request do
let!(:campaign) { create :campaign }
let!(:user) { campaign.user }
let!(:user_info) { user.user_info }
let!(:advertiser_payment_preference) { create(:advertiser_payment_preference, advertiser_user_id: user.id)}
before :each do
login(user)
end
describe "PUT/PATCH /advertiser/billing/settings" do
context "it is a valid request" do
it "updates the resource" do
binding.pry
new_advertiser_payment_preference = build(:advertiser_payment_preference_sequence)
binding.pry
params = {}
params['user_info'] = to_api_advertiser_info(new_advertiser_user_info)
api_put '/advertiser/billing/profile', params
expect(response.status).to eq(200)
user_info.reload
expect(user_info.organization).to eq(new_advertiser_user_info.organization)
expect(user_info.phone).to eq(new_advertiser_user_info.phone)
expect(user_info.fiscal_code).to eq(new_advertiser_user_info.fiscal_code)
expect(user_info.country).to eq(new_advertiser_user_info.country)
expect(user_info.address).to eq(new_advertiser_user_info.address)
end
end
end
end
This is how the test for the controller looks like . When I call new_advertiser_payment_preference = build(:advertiser_payment_preference_sequence) I get an object user_info with "" on all fields that are made using advertser_user_info_sequence.

Related

FactoryBot creates records even when a record is provided

I use factorybot to create records in development sometimes. However, it's creating a bunch of extra data that I wasn't expecting.
I have two factories:
FactoryBot.define do
factory :user do
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
email { "#{first_name}.#{last_name}#example.com" }
end
factory :post do
user { create(:user) }
title { Faker::Lorem.sentence }
end
end
If I run FactoryBot.create(:post) in the rails console, it will create a new post with a new user. What I didn't expect is that if I do FactoryBot.create(:post, user: User.first), it would create a post associated with the first user, but still create a new record. So, I get this:
irb(main):001:0> User.count
=> 1
irb(main:002:0> FactoryBot.create(:post, user: User.first)
=> #<Post id: 1, title: 'Lorem Ipsum', host_id: 1>
irb(main:003:0> User.count
=> 2
Everything works, it just creates a new user record that isn't attached to anything. Is there anyway to stop that from happening?
You don't need to tell FactoryBot to create(:user). Just remove it.
factory :post do
user
title { Faker::Lorem.sentence }
end
https://devhints.io/factory_bot

ActiveModel::Serializers::JSON - How to map an array of JSON from a returned string to an collection of Ruby objects

I have code that works for a single instance but the API I am consuming returns an array of data. I have a class to encapsulate this data:
class Brewery
include ActiveModel::Serializers::JSON
attr_accessor :id, :name
def attributes=(hash)
hash.each { |key, value| send("#{key}=", value) }
end
def attributes
instance_values
end
end
And what the returned data looks like is similar to this
[
{
"id": 2,
"name": "Avondale Brewing Co"
},
{
"id": 44,
"name": "Trim Tab Brewing"
}
]
I can marshal a single JSON hash to the class with code such as this:
brewery = Brewery.new
brewery.from_json(single_brewery)
However this doesn't work with the array. I'm relatively new with Ruby so I'm not quite sure what the function to use is or to at least complete the JSON hashes to an array I can map from_json over.
This works but seems clunky
breweries = JSON.parse(brewery_list).map { |b|
brewery = Brewery.new
brewery.from_json(b.to_json)
}
I am unsure why do you find mapping an array clunky, but you might turn your Brewery to be a factory.
class Brewery
...
def self.many(brewery_list)
JSON.parse(brewery_list).
map(&:to_json).
map(&Brewery.new.method(:from_json)
end
end
And use it like this
breweries = Brewery.many(brewery_list)

overwrite let inside an it method to alter params send to subject

It's possible to overwrite a let value inside an spec? I wanted to be able to set subject and modify my params within each test, something like:
subject {
MyClass.new params
}
let(:params) { {} }
describe '#initialize' do
it 'should set new params' do
params = {a: 1}
expect{ subject }.to do_something
end
it 'should raise with string' do
params = 'string'
expect{ subject }.to raise_error
end
end
or what is the correct way to approach this? should I wrote my expect{} with the whole class name?
I would rewrite the specs like this:
subject { -> { MyClass.new(params) } } # Note: subject is a lambda
describe '#initialize' do
context 'with blank params' do
let(:params) { {} }
it { is_expected.to do_something }
end
context 'with string params' do
let(:params) { 'string' }
it { is_expected.to raise_error }
end
end
But - as max pleaner already said - in this simple example it would probably be more readable and maintainable to skip the subject and just use the MyClass.new ... call directly in the expectation

How to refactor RSpec tests for API

I've got a series of RSpec tests for a Sinatra based API, and would like to refactor them to make them a little simpler and reduce repetition.
Here's an example a test for a route:
describe 'post /sections with empty data' do
before do
params = {
:site_id => site.id,
:page_id => page.id,
}
post '/sections', params, #session
end
specify { last_response.status.should == 200 }
specify { json_response['id'].should_not be_nil }
specify { json_response['type'].should == default_section_type }
end
Each test will be using the same base URL, with the same session data, the only difference is the parameters, and what the responses should be. There's at least 4 tests (GET, POST, PUT, DELETE) per route, and usually more.
Is there a way of making these tests more manageable?
Without resorting to metaprogramimng, you can use nested describe blocks to override only the parameters you want:
describe "/sessions" do
before do
send(http_method, "/sessions", params, #session)
end
describe "with POST" do
let(:http_method) { :post }
describe "and empty data" do
let(:params) do
{ :site_id => site.id, :page_id => page.id }
end
specify { last_response.status.should == 200 }
specify { json_response['id'].should_not be_nil }
specify { json_response['type'].should == default_section_type }
end
describe "with non-empty data" do
let(:params) do
# relevant params
end
end
end
describe "with GET" do
let(:http_method) { :get }
# ...
end
end
Have no idea if this works but it can give you an idea of what you can do
describe ' /sections with empty data' do
before(:all) do
#params = {
:site_id => site.id,
:page_id => page.id,
}
end
after(:each) do
specify { last_response.status.should == 200 }
specify { json_response['id'].should_not be_nil }
specify { json_response['type'].should == default_section_type }
end
[:get, :post, :put, :delete].each do |http_method|
it "works with #{http_method}" do
send(http_method) '/sections', #params, #session
end
end
end
Update
Reading your question again made me realize that this is not what you actually asked for. If it doesn't help at all tell me so I delete it.

RSpec method testing

I am trying to test some methods in my models. For example,
in my model
def name
self.first_name + " " + self.last_name
end
I want to test it but I cannot do. How can I test this method in my model_spec.rb file?
Something like this, perhaps?
describe YourModel do
subject { YourModel.new(first_name: "Some", last_name: "Guy) }
its(:first_name) { should eql "Some" }
its(:last_name) { should eql "Guy" }
its(:name) { should eql "Some Guy" }
end
You could also use =~ and a regular expression, but I find that a little noisy.

Resources