How to write unit test for serializer using rspec - ruby-on-rails

Hi i am working on a RoR project with ruby-2.5.0 and rails 5. I have two models Receipt and Receipt Items. Receipt has_many receipt_items.
Receipt Serializer:-
# frozen_string_literal: true
class ReceiptSerializer
include JSONAPI::Serializer
TYPE = 'reciept'
attribute :name
attribute :receipt_date
attribute :address
attribute :total_paid
attribute :user_id
attribute :receipt_date do
object.receipt_date.strftime('%d/%m/%Y %H:%M:%S')
end
attribute :receipt_items do
object.receipt_items.map do |receipt_item|
::ReceiptItemSerializer.new(receipt_item).attributes
end
end
end
Receipt Items Serializer:-
# frozen_string_literal: true
class ReceiptItemSerializer
include JSONAPI::Serializer
TYPE = 'reciept_item'
attribute :item_name
attribute :quantity
attribute :price
end
I have written the unit test for both the serializers as follows:-
receipt_serializer_spec.rb
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ReceiptSerializer do
let(:id) { 1 }
let(:user_id) { 1 }
let(:name) { 'IGA' }
let(:address) { 'address' }
let(:total_paid) { '100' }
let(:receipt_date) { '12/04/2018 15:36:00' }
let(:receipt) do
Receipt.new(
user_id: user_id,
name: name,
address: address,
total_paid: total_paid,
receipt_date: receipt_date
)
end
subject { JSONAPI::Serializer.serialize(receipt) }
it { is_expected.to have_jsonapi_attributes('user-id' => user_id) }
it { is_expected.to have_jsonapi_attributes('address' => address) }
it { is_expected.to have_jsonapi_attributes('total-paid' => total_paid) }
it { is_expected.to have_jsonapi_attributes('receipt-date' => receipt_date) }
it { is_expected.to have_jsonapi_attributes('receipt-date' => receipt_date) }
end
receipt_item_serializer_spec.rb
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ReceiptSerializer do
let(:receipt_id) { 1 }
let(:item_name) { 'ABC' }
let(:quantity) { 1 }
let(:price) { '100' }
let(:receipt_item) do
ReceiptItem.new(
receipt_id: receipt_id,
item_name: item_name,
quantity: quantity,
price: price
)
end
subject { JSONAPI::Serializer.serialize(receipt_item) }
it { is_expected.to have_jsonapi_attributes('item-name' => item_name) }
it { is_expected.to have_jsonapi_attributes('quantity' => quantity) }
it { is_expected.to have_jsonapi_attributes('price' => price) }
end
Now i don't know how to write unit test for receipt_items attribute which i have defined in the receipt_serializer.rb. Please help me. Thanks in advance.

You could write
it "serializes the receipt" do
expect(subject).to include('item-name' => 'ABC', 'item-price' => '100') # ... etc
end
Not sure there's huge value to testing this, if you're individually testing the presence of all your attributes already.

I hope this gem will be useful for you https://github.com/collectiveidea/json_spec

Related

How to write Rspec test case?

I am beginner to Rails Rspec and not getting how to write testing for below code. I have scope in my Trip model as shown below
scope :pending_end_trips, -> {
joins(:booking).
where(state: :active).
where(owners_end_at: nil).
where("bookings.end_at < ? ", 1.hour.ago)
}
Here is my code:
require 'rails_helper'
RSpec.describe Trip, type: :model do
fixtures :all
let(:user_1) { users(:user_1) }
let(:user_2) { users(:user_2) }
let(:contact_1) { contacts(:contact_1).tap{ |c| c.update_columns(user_id: user_1.id) } }
let(:contact_2) { contacts(:contact_2).tap{ |c| c.update_columns(user_id: user_2.id) } }
let(:rider) { riders(:rider_1).tap{ |r| r.update_columns(user_id: user_1.id) } }
let(:owner) { owners(:owner_1).tap{ |o| o.update_columns(user_id: user_2.id) } }
let(:motorcycle) { motorcycles(:motorcycle_basic_1).tap{ |m| m.update_columns(owner_id: owner.id) } }
let(:booking) { bookings(:booking_1).tap{ |b| b.update_columns(motorcycle_id: motorcycle.id, owner_id: owner.id, rider_id: rider.id) } }
let(:request) { requests(:request_1).tap{ |r| r.update_columns(booking_id: booking.id, motorcycle_id: motorcycle.id) } }
let(:trip) { trips(:trip_1).tap{ |t| t.update_columns(booking_id: booking.id) } }
context 'scopes' do
describe 'pending_end_trips' do
it 'returns trips which are supposed to end in past 1 hour' do
trip.update_columns(owners_end_at: nil, state: :active)
expect(described_class.pending_end_trips).to include trip
trip.update_columns(owners_end_at: nil, state: :complete)
expect(described_class.pending_end_trips).not_to include trip
trip.update_columns(owners_end_at: nil, state: :canceled)
expect(described_class.pending_end_trips).not_to include trip
trip.update_columns(owners_end_at: nil, state: :deleted)
expect(described_class.pending_end_trips).not_to include trip
trip.update_columns(owners_end_at: nil, state: :pending)
expect(described_class.pending_end_trips).not_to include trip
end
end
end
end
Getting the below error:
It doesn't fit the conditions because you don't specify end_at for booking
let(:booking) { bookings(:booking_1).tap{ |b| b.update_columns(end_at: Time.now - 2.hours, motorcycle_id: motorcycle.id, owner_id: owner.id, rider_id: rider.id) } }

Rails RSpec (beginner): Why is this test sometimes passing and sometimes not?

I have a book database where books can have different book formats (hardcover, softcover etc).
I have factories with factory_bot.
The following spec just run through with an error - and then when I run it the second time, it worked. I have no idea where I need to start searching....
The error was:
1) BookFormat displays the proper book format for a book with that format
Failure/Error: expect(#book.book_format.name).to eq('Hardcover')
expected: "Hardcover"
got: "Not defined"
Here is the full spec:
require 'rails_helper'
RSpec.describe BookFormat, type: :model do
before(:all) do
#book = create(:hobbit)
#book_format_default = create(:not_defined)
end
it 'displays the proper book format for a book with that format' do
expect(#book.book_format.name).to eq('Hardcover')
end
it 'should reassign to the fallback book_format if their book_format is deleted' do
format = #book.book_format
format.destroy
expect(#book.reload.book_format.id).to eq(#book_format_default.id)
end
it 'should not let the fallback format be deleted' do
format = #book_format_default
format.destroy
expect(format).to be_truthy
end
end
Here is the corresponding factor for the book :hobbit:
factory :hobbit, class: Book do
title { 'The Hobbit' }
year { 1937 }
rating { 5 }
condition { 4 }
synopsis { "<p>#{Faker::Lorem.paragraphs(number: 30).join(' ')}</p>" }
association :book_format, factory: :hardcover
association :user, factory: :me
genres { [ create(:fiction) ] }
after(:build) do |hobbit|
hobbit.cover.attach(
# rubocop:disable Rails/FilePath
io: File.open(Rails.root.join('db', 'sample', 'images', 'cover-1.jpg')),
# rubocop:enable Rails/FilePath
filename: 'cover.jpg',
content_type: 'image/jpeg'
)
end
end
And here are the factories for book_formats:
FactoryBot.define do
factory :not_defined, class: BookFormat do
name { 'Not defined'}
fallback { true }
end
factory :hardcover, class: BookFormat do
name { 'Hardcover' }
end
factory :softcover, class: BookFormat do
name { 'Softcover' }
end
end

How can i resolve o problem Failure/Error: let(:currency) { create(:currency) } using Rspec

I am testing model using rspec and factory Girl
My model
class Currency < ActiveRecord::Base
has_many :countries
validates :name, presence: true
validates :name, uniqueness: true
before_destroy :safe_to_delete
def safe_to_delete
countries.any? ? false : true
end
end
My factory girl
FactoryGirl.define do
factory :currency, class: 'Currency' do
sequence(:name) { |i| "Currency-#{i}" }
end
end
My currency_spec.rb is
require 'rails_helper'
describe Currency , type: :model do
let(:currency) { create(:currency) }
let(:currency1) { create(:currency) }
let(:country) { create(:country) }
describe 'associations' do
subject {currency}
it { should have_many(:countries) }
end
describe 'validations' do
subject {currency}
it { should validate_presence_of(:name) }
it { should validate_uniqueness_of(:name) }
end
describe 'method save_to_delete' do
context 'case false' do
before { country.update_column(:currency_id, currency.id) }
subject { currency.destroy }
it { is_expected.to be_falsy }
end
context 'case true' do
before { country.update_column(:currency_id, currency1.id) }
subject { currency.destroy }
it { is_expected.to be_truthy }
end
end
end
The error is:
Failure/Error: let(:currency) { create(:currency) }
ActiveRecord::RecordInvalid:
A validação falhou: Name não está disponível
Even though I disable the presence and uniqueness validations in the model, the problem continues
Who can help me
Did you properly create the migration to include the name on currencies at database level?
Because I created the migration here locally and the tests passed.
Please take a look on the below code.
It is what I did locally and is working here!
1. Migration
file: db/migrate/2021XXXXXXXXXX_create_currencies.rb
class CreateCurrencies < ActiveRecord::Migration
def change
create_table :currencies do |t|
t.string :name
end
end
end
2. Model
app/models/currency.rb
class Currency < ActiveRecord::Base
has_many :countries
validates :name, presence: true, uniqueness: true # Can be oneliner ;)
before_destroy :safe_to_delete
def safe_to_delete
countries.empty? # Much simpler, right? ;)
end
end
3. Factory
spec/factories/currency.rb
FactoryGirl.define do
factory :currency do
sequence(:name) { |i| "Currency-#{i}" }
end
end
4. Tests
spec/models/currency_spec.rb
require 'rails_helper'
describe Currency, type: :model do
let(:currency1) { create(:currency) }
let(:currency2) { create(:currency) }
let(:country) { create(:country) }
describe 'associations' do
subject { currency1 }
it { should have_many(:countries) }
end
describe 'validations' do
subject { currency1 }
it { should validate_presence_of(:name) }
it { should validate_uniqueness_of(:name) }
end
describe 'when the currency is being deleted' do
context 'with countries associated' do
before { country.update_column(:currency_id, currency1.id) }
subject { currency1.destroy }
it { is_expected.to be_falsy }
end
context 'with no countries associated' do
before { country.update_column(:currency_id, currency2.id) }
subject { currency1.destroy }
it { is_expected.to be_truthy }
end
end
end
Test Execution
Finally, the tests should work correctly with the above setup!
spec/models/currency_spec.rb
rspec spec/models/currency_spec.rb
D, [2021-03-06T03:31:03.446070 #4877] DEBUG -- : using default configuration
D, [2021-03-06T03:31:03.449482 #4877] DEBUG -- : Coverband: Starting background reporting
.....
Top 5 slowest examples (0.10688 seconds, 11.4% of total time):
Currency when the currency is being deleted with countries associated should be falsy
0.04095 seconds ./spec/models/currency_spec.rb:23
Currency associations should have many countries
0.03529 seconds ./spec/models/currency_spec.rb:10
Currency when the currency is being deleted with no countries associated should be truthy
0.01454 seconds ./spec/models/currency_spec.rb:29
Currency validations should validate that :name cannot be empty/falsy
0.00812 seconds ./spec/models/currency_spec.rb:15
Currency validations should validate that :name is case-sensitively unique
0.00797 seconds ./spec/models/currency_spec.rb:16
Finished in 0.93948 seconds (files took 8.04 seconds to load)
5 examples, 0 failures
All tests passed ✅

undefined method ` =' for #<Agency id: nil, name: nil, ip_adress: nil>

I am writing some rspec tests on my app and there is a bug, it show me that error :
undefined method ` =' for #<Agency id: nil, name: nil, ip_adress: nil>
There is my tests :
require 'rails_helper'
RSpec.describe Agency, type: :model do
it "should create the agency if all fields are filled" do
expect(FactoryGirl.build(:agency)).to be_valid
end
it "should fail if name is missing" do
expect(FactoryGirl.build(:agency, name: nil)).to_not be_valid
end
it "should fail if ip_adress is missing" do
expect(FactoryGirl.build(:agency, ip_adress: nil)).to_not be_valid
end
it "should fail if there is a double name in db" do
agency = FactoryGirl.create(:agency)
expect(FactoryGirl.build(:agency, name: agency.name)).to_not be_valid
end
end
My agency model :
class Agency < ActiveRecord::Base
module Agencymod
attr_accessor :name, :ip_adress
end
has_many :users
has_many :incidents
has_many :field_agency_agencies, dependent: :destroy
has_many :field_agencies, through: :field_agency_agencies
# # Regexp for the postal code.
# cp_regexp = /\A((0[1-9])|([1-8][0-9])|(9[0-8])|(2A)|(2B))[0-9]{3}\z/
# # Regexp for email.
# email_regexp = /\A[a-zA-Z0-9._-]+#[a-z0-9._-]{2,}\.[a-z]{2,4}$\z/
# # Regexp for phone number.
# phone_regexp = /\A(0|\+33|0033)[1-9][0-9]{8}\z/
# # Regexp for ip address.
ip_regexp = /\A(?:(?:[1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}(?:[1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\z/
validates :name, presence: true,
uniqueness: { case_sensitive: false }, length: { in: 0..44 }
validates :ip_adress, presence: true, format: { with: ip_regexp }, length: { in: 0..49 }
end
And finally my factory :
factory :agency, class: Agency do |f|
f.name  { Faker::Address.city }
f.ip_adress "8.8.8.8"
end
It is the first time this error appear and when I have tested the user model it works very well...
Sorry for my poor english :)
Thanks to everyone post answer.
I've found the error it appear there are an space between the f.name and the { Faker::Address.city }
When I have removed it, it show me the same error but with undefined method'name='... so I writed the factory like that :
f.name Faker::Address.city
and it works very well ...
My others factories are writted as below :
factory :user do |f|
f.surname { Faker::Name.first_name }
f.name { Faker::Name.last_name }
f.pseudo { Faker::Internet.user_name }
f.password "password"
f.email { Faker::Internet.free_email }
f.type_user_id 23
f.agency_id 2
f.tel "0606060606"
f.ip_addr { Faker::Internet.ip_v4_address }
end
and are working well too ..
f.name "TEST" # That works !
f.name"TEST" # Works too
f.name Faker::Address.city # Works
f.name { Faker::Address.city } # Nope
Issue in gem ?

"normalize_opts expects a string or a hash" error testing Stripe in Rails

Im trying to create a test for a method that make a charge in a credit card using Stripe, I have set all the Stripe configurations and already got working other tests like "create stripe customer" and "assign bank account" but when I try to create a charge the test show me the next failure:
1) STRIPE API POST /v1/events/:id/pay when the event exist pay event
Failure/Error: #charge = Stripe::Charge.create( charge_attrs, user.secret_key )
TypeError:
normalize_opts expects a string or a hash
# /.rvm/gems/ruby-2.4.0/gems/stripe-1.58.0/lib/stripe/util.rb:203:in `normalize_opts'
Gemfile
gem 'rails', '~> 5.0.1'
gem 'stripe', '~> 1.31'
gem 'stripe-ruby-mock', '~> 2.4.0', :require => 'stripe_mock'
stripe_spec.rb
RSpec.describe 'STRIPE API', type: :request do
describe 'POST /v1/events/:id/pay' do
let!(:events) { create_list(:event, 2) }
let(:event_id) { events.first.id }
let!(:users) { create_list(:user, 2) }
let(:user_id) { users.first.id }
let(:auth_headers) { users.first.create_new_auth_token }
let(:stripe_helper) { StripeMock.create_test_helper }
context 'when the event exist' do
before {
StripeMock.start
card_token = StripeMock.generate_card_token(last4: "9191", exp_year: 2020)
post "/v1/events/#{event_id}/pay", params: { token: card_token, user_id: user_id } , headers: auth_headers
}
after { StripeMock.stop }
it "pay event" do
p json
expect(response).to be_success
end
end
end
end
events_controller
module Api::V1
class EventsController < ApiController
#POST events/{id}/pay/{user_id, token}
def pay
#event = Event.all.active.find(params[:id])
# Find the user to pay.
user = User.find( params[:id] )
# Charge fee.
amount = 10
user.currency = 'USD'
# Calculate the fee amount that goes to the application.
fee = (amount * Rails.application.secrets.fee_percentage).to_i
begin
charge_attrs = {
amount: amount,
currency: user.currency,
source: params[:token],
description: "Pickleball Clinic",
application_fee: fee
}
# Use the user-to-be-paid's access token
# to make the charge directly on their account
#charge = Stripe::Charge.create( charge_attrs, user.secret_key )
json_response(#charge)
rescue Stripe::CardError => e
#error = e.json_body[:error][:message]
json_response(#error)
end
end
end
event.rb
class Event < ApplicationRecord
validates_presence_of :name
validates_presence_of :description
validates_presence_of :created_by
belongs_to :admin, :class_name => 'User', :foreign_key => 'created_by'
scope :active, -> { where( is_active: true ) }
has_many :event_participants
has_many :participants, :through => :event_participants, foreign_key: "participant_id" do
def active
where("event_participants.is_active = ?", true)
end
end
end
FactoryGirl
FactoryGirl.define do
factory :event do
association :admin, factory: :user
name { Faker::Lorem.sentence }
description { Faker::Lorem.paragraphs(2) }
status { Faker::Number.between(0,3) }
fee { Faker::Number.between(5,10) }
created_at { Faker::Date.between(2.days.ago, Date.today) }
updated_at { Faker::Date.between(1.days.ago, Date.today) }
is_active { 1 }
association :location, factory: :location
after(:create) do |event|
create_list(:event_participants, 3, event: event)
end
end
end
I appreciate this is an old question, but I just hit the same error and ended up here.
For those of you who do the same, you may discover that – like me – the Stripe Account ID is blank, or more correctly nil, and thus it is not the String or the Hash expected (as the error message refers to).
In the Question's code, I believe this would be the user.secret_key, as it looks like this implementation is for Stripe's Connect product.

Resources