How to write Rspec test case? - ruby-on-rails

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) } }

Related

Method in model is ignored by test rspec rails

I have a method in my model that group items by variant id if multiple items have the same variant_id she merge them and add their quantity.
My model:
class ShoppingCart < ApplicationRecord
belongs_to :company
belongs_to :user
has_many :items, class_name: "ShoppingCartItem", dependent: :destroy
accepts_nested_attributes_for :items, reject_if: proc { |attributes| attributes['quantity'].blank? }
before_validation :remove_invalid_items
before_validation :merge_items
def merge_items
self.items = items.group_by { |i| i[:variant_id] }.map do |variant_id, is|
quantity_sum = is.sum { |i| i[:quantity] }
ShoppingCartItem.new(variant_id: variant_id, quantity: quantity_sum)
end
end
end
This method works well when i try it manually but in my tests rspec seems to ignore this method
My tests:
require 'rails_helper'
RSpec.describe ShoppingCart, type: :model do
describe "associations" do
it { is_expected.to belong_to(:company) }
it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:items) }
end
describe "merge_items" do
let(:shopping_cart) { create(:shopping_cart) }
it "merge items if same variant_id" do
existing_item = create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "same variant_id", quantity: 1)
item = create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "same variant_id", quantity: 1)
expect(shopping_cart.reload.items.count).to eq(1)
end
it "not merge items if variant_id different" do
existing_item = create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "variant_id", quantity: 1)
item = create(:shopping_cart_item, shopping_cart: shopping_cart, variant_id: "different variant_id", quantity: 1)
expect(shopping_cart.reload.items.count).to eq(2)
end
end
end
Tests output:
Failure/Error: expect(shopping_cart.reload.items.count).to eq(1)
expected: 1
got: 2
I changed the code and this works:
require 'rails_helper'
RSpec.describe ShoppingCart, type: :model do
describe "associations" do
it { is_expected.to belong_to(:company) }
it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:items) }
end
describe "merge_items" do
let(:shopping_cart) { create(:shopping_cart) }
context "same variant_id" do
let(:params) {{ shopping_cart: {items_attributes: [{variant_id: 'same variant_id', quantity: 2}, {variant_id: 'same variant_id', quantity: 2}]}}}
before do
shopping_cart.update params[:shopping_cart]
end
it "create just one item" do
expect(shopping_cart.reload.items.count).to eq(1)
end
it "adds all quantities" do
expect(shopping_cart.reload.items.last.quantity).to eq(4)
end
end
context "not same variant_id" do
let(:params) {{ shopping_cart: {items_attributes: [{variant_id: 'variant_id', quantity: 2}, {variant_id: 'different variant_id', quantity: 2}]}}}
before do
shopping_cart.update params[:shopping_cart]
end
it "not merge items if variant_id different" do
expect(shopping_cart.reload.items.count).to eq(2)
end
end
end
end

Updating belongs_to association with strong params

I've been using attr_accessible for a long time, and I'm struggling a bit adopting to strong params.
models
class Rule
end
class Account
belongs_to :applied_rule, class_name: 'Rule', foreign_key: 'rule_id', inverse_of: false, optional: true
accepts_nested_attributes_for :applied_rule, update_only: true, allow_destroy: true
end
I'm trying to update the relation, and not having much success. With attr_accessible you would expose the relation itself, then use something like #account.update(applied_rule: #rule) and it would just work™.
controllers
class AccountsController
def update
if #account.update(account_params)
render json: AccountSerializer.new(#account)
else
render json: #account.errors, status: :unprocessable_entity
end
end
private
def account_params
params.require(:account).permit(:name, applied_rule_attributes: %i(id _destroy))
end
end
specs
RSpec.describe 'Accounts', type: :request do
let(:account) { create(:account) }
describe 'PUT /accounts/:id' do
before { put account_path(account, params.merge(format: :json)) }
let(:rule) { create(:rule) }
context 'with good params' do
let(:params) { { account: { applied_rule_attributes: { id: rule.id } } } }
it { expect(response).to have_http_status(:ok) }
it { expect(account.changed?).to be true }
it { expect(account.applied_rule).to eq rule }
end
context 'when deleting relation' do
let(:params) { { account: { applied_rule_attributes: { _destroy: true } } } }
it { expect(response).to have_http_status(:unprocessable_entity) }
it { expect(account.changed?).to be true }
it { expect(account.applied_rule).to be_nil }
end
end
end
I tried this originally without the nested attributes - it still doesn't work, but I feel like it's heading in the correct direction.
I'd like to change the relation on the entity. I want to set the applied rule on an account to something different, or maybe even remove the applied rule from the account entirely (without deleting the rule, just the association). Is there an idiomatic approach to this?
interesting code
> params[:account][:applied_rule] = Rule.friendly.find(params[:account][:rule_id])
> params
=> <ActionController::Parameters {"account"=><ActionController::Parameters {"rule_id"=>"065230e1cb530d408e5d", "applied_rule"=>#<Rule id: 1, account_id: 3, name: "Rule 1", global: false, created_at: "2018-10-12 00:55:49", updated_at: "2018-10-12 00:55:49", slug: "065230e1cb530d408e5d">} permitted: false>, "controller"=>"accounts", "action"=>"update", "id"=>"account-2", "format"=>"json"} permitted: false>
> params.require(:account).permit(:name, :applied_rule)
=> <ActionController::Parameters {} permitted: true>

rspec flow isn't understandable

In my acceptance test, I want to test the value of gift.available_quantity which is virtual attribute:
def available_quantity
pendings_orders = Order.where(status: 'pending')
order_lines_pending = OrderLine.where(order_id: pendings_orders)
gift_quantity_pending = order_lines_pending.sum do |oline|
return oline.quantity if oline.gift_ean13 == ean13
end
quantity - gift_quantity_pending
end
I don't test my code, but it's not the problem.
The problem is that my rspec evaluate the value of my gift.available_quantity before creating my fake order:
require 'acceptance_helper'
resource 'Gifts' do
header 'Accept', 'application/json'
header 'Content-Type', 'application/json'
header 'Authorization', :authorization
get '/api/v1/gifts' do
let!(:member) { create(:member, id: 1) }
let(:user) { create(:user, :activated, member: member) }
let(:token) { Knock::AuthToken.new(payload: { sub: user.id }).token }
let(:authorization) { "Bearer #{token}" }
let!(:delivery_address) { create(:delivery_address, :favorited, member: member) }
let!(:gifts) { create_list(:gift, 2, :enabled, quantity: 4) }
let(:order_lines) { create(:order_line, gift_ean13: gifts.first.ean13, quantity: 1, order_id: order.id) }
let(:order_lines2) { create(:order_line, gift_ean13: gifts.last.ean13, quantity: 2, order_id: order.id) }
let!(:order) { create(:order, member: member, delivery_address: delivery_address) }
example_request 'Get gift list' do
expected_response = {
gifts: [
{
id: gifts.first.id,
name: gifts.first.name,
description: gifts.first.description,
reward_price: gifts.first.reward_price,
quantity: gifts.first.quantity,
date_from: gifts.first.date_from,
date_to: gifts.first.date_to,
image: nil,
quantity_alert: gifts.first.quantity_alert,
available_quantity: 3
},
{
id: gifts.last.id,
name: gifts.last.name,
description: gifts.last.description,
reward_price: gifts.last.reward_price,
quantity: gifts.last.quantity,
date_from: gifts.last.date_from,
date_to: gifts.last.date_to,
image: nil,
quantity_alert: gifts.last.quantity_alert,
available_quantity: 2
}
]
}
expect(status).to eq(200)
expect(response_body).to eq(expected_response.to_json)
end
end
end
My order isn't created before, so gift.avalaible_quantity isn't good...
Can someone help me understand "RSpec flow"?
I think the problem is that your order_lines aren't being created before the spec runs.
Use let! to create them instead so they exist when the available_quantity method runs.
let!(:order_lines) { create(:order_line, gift_ean13: gifts.first.ean13, quantity: 1, order_id: order.id) }
let!(:order_lines2) { create(:order_line, gift_ean13: gifts.last.ean13, quantity: 2, order_id: order.id) }

How to write unit test for serializer using rspec

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

rspec testing instance methods

How can I test these instance methods with rspec and factory?
factories
FactoryGirl.define do
factory :user do
sequence(:email) { |n| "example#{n}#gmail.com"
password 'example0000'
password_confirmation 'example0000'
new_chat_notification { Faker::Number.between(0, 10) }
new_other_notification { Faker::Number.between(0, 10) }
end
factory :notification do
notifiable_id { Faker::Number.between(1, 10) }
notifiable_type { Faker::Lorem.word }
action { Faker::Hipster.word }
checked_at { Faker::Time.between(DateTime.now - 2, DateTime.now - 3) }
association :sender, factory: :user
association :recipient, factory: :user
end
end
user.rb
#check and decrease chat notification that happens between 2 given users (max 1)
def decreasing_chat_notification_number(user)
notification = self.notifications.between_chat_recipient(user).unchecked.first
self.checking_and_decreasing_notification(notification) if notification.present?
end
#check and decrease task notifications that happens between 2 given users
def decreasing_task_notification_number(user)
self.notifications.task.between_other_recipient(user).unchecked.each do |notification|
self.checking_and_decreasing_notification(notification)
end
end
UPDATE
user.rb (here is the method that is called)
def checking_and_decreasing_notification(notification)
notification.check_notification
if notification.notifiable_type == "Message"
decrease_new_chat_notifications
decreased_chat_number_pusher
else
decrease_new_other_notifications
decreased_other_number_pusher
end
end
user_spec.rb
let(:sender) { create(:user) }
let(:user) { create(:user) }
let(:notification) { create(:notification, notifiable_type: "Message", recipient: user, sender: sender) }
it "decreasing_chat_notification_number" do
allow(user).to receive(:checking_and_decreasing_notification).with(notification)
user.decreasing_chat_notification_number(sender)
expect(user).to receive(:checking_and_decreasing_notification).with(notification)
end
error message:
1) User instance methods decreasing_chat_notification_number
Failure/Error: expect(user).to receive(:checking_and_decreasing_notification).with(notification)
(#<User:0x007fefcfd6ce20>).checking_and_decreasing_notification(#<Notification id: 1,
recipient_id: 1, created_at: "2016-04-14 19:47:36", updated_at: "2016-04-14 19:47:36",
sender_id: 2, checked_at: "2016-04-12 02:32:50", notifiable_id: 4, notifiable_type: "Message", action: "tilde">)
expected: 1 time with arguments: (#<Notification id: 1,
recipient_id: 1, created_at: "2016-04-14 19:47:36", updated_at: "2016-04-14 19:47:36",
sender_id: 2, checked_at: "2016-04-12 02:32:50", notifiable_id: 4, notifiable_type: "Message", action: "tilde">)
received: 0 times
(sidenote) You do not need self in your methods.
Take a look:
def decreasing_chat_notification_number(user)
notification = notifications.between_chat_recipient(user).unchecked.first
checking_and_decreasing_notification(notification) if notification.present?
end
describe '#decreasing_chat_notification_number' do
let(:notification) { create(:notification) }
let(:user) { create(:user) }
subject { create(:user) }
it 'your description' do
expect(subject).to receive(:checking_and_decreasing_notification).with(notification)
subject.decreasing_chat_notification_number(user)
end
end

Resources