rspec flow isn't understandable - ruby-on-rails

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

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

Ruby on Rails - Is this a good way to eliminate duplicate code in RSpec?

I wrote this code for testing controller update function.
Wrote a method for eliminating duplicate code.
Is this an explicit way to do it?
users_controller_spec.rb
context 'Update failed' do
def render_edit
user.reload
expect(response.status).to eq(200)
end
it 'Name is nil' do
put :update, params: { id: user.id, user: { name: '' } }
render_edit
end
it 'Email is exist' do
create(:user, email: 'user#gmail.com')
put :update, params: { id: user.id, user: { email: 'user#gmail.com' } }
render_edit
end
it 'Email is nil' do
put :update, params: { id: user.id, user: { email: '' } }
render_edit
end
it 'Password must be at least 8 characters' do
put :update, params: { id: user.id, user: { password: '1234567', password_confirmation: '1234567' } }
render_edit
end
it 'Passwords do not match' do
put :update, params: { id: user.id, user: { password: '1234567890', password_confirmation: '123456789' } }
render_edit
end
end
I was thinking to use after(:each). But it looks a little wired in logic.
Or use loop to replace params.
Any suggestion?
You can use shared examples as suggested in the comments, but there's an easier way.
context 'Update failed' do
before do
put :update, params: params
user.reload # I'm not sure why you need this
end
subject { response }
context 'Name is nil' do
let(:params} { {id: user.id, user: { name: '' }} }
it { is_expected.to be_success }
end
context 'Email exists' do
let(:params) { { id: user.id, user: { email: 'user#gmail.com' } }
let(:user) { create(:user, email: 'user#gmail.com') }
it { is_expected.to be_success }
end
# and so on
end
The main rune I use is - make it obvious what change in each context. So instead of redefining put ..., extract it as a let and define it per context.
be_success is part of rspec magic, wherever you use be_something matcher it'll try to use something? method and check if it's true, i.e.
expect(foo).to be_empty? == expect(foo.empty?).to eq(true)
If you don't want it make it like this
subject { response.status }
# and later
is_expected.to eq 200
is_expected.to is just a shorthand for expect(subject).to

Nested json object with Jbuilder

What I want:
{
status: 200,
response:
{
user: { id: 1, name: 'Bob', city: 'New York' }
}
}
What I have tried:
json.status 200
json.user json.(#user, :id, :name, :city)
But it does not give the desired results:
{
"status" : "200"
"response" : "{"status":200,"id":1, name: "bob", city: "New York","user":["id","name","city"]}"
}
What am I doing wrong?
Try this:
json.status 200
json.response do
json.user #user.as_json(only: [:id, :name, :city])
end

How can i map one query result to another query result in ruby on rails

Hi I am new to Ruby on Rails development. I have two queries with different model. My first_query is get from question model and second query is get from favourite model. I want to map with a column user_favourite from second query result to first query result.
this is my controller queries
def index
#first_query = Question.order('created_at DESC').page(params[:page]).per( (ENV['ILM_QUESTIONS_PER_PAGE'] || 5).to_i )
#second_query=Favourite.with_user_favourite(#user)
#combined_queries = #first_query + #second_query
end
favourite.rb
scope :with_user_favourite, -> (user) {
joins(:user).
where(favourites: {user_id: user})
}
index.json.builder
json.questions #combined_events
json for the result is
{
questions: [ #this is first query result
{
id: 88,
user_id: 28,
content: "test32",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
}
},
{
id: 87,
user_id: 18,
content: "testing riyas",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
}
},
{ #this is second query result
id: 1,
user_id: 2,
question_id: 84,
created_at: "2016-05-12T06:51:54.555-04:00",
updated_at: "2016-05-12T06:51:54.555-04:00"
},
{
id: 2,
user_id: 2,
question_id: 81,
created_at: "2016-05-12T07:23:47.770-04:00",
updated_at: "2016-05-12T07:23:47.770-04:00"
}
]
}
i want response like
{
questions: [
{ #first query result
id: 88,
user_id: 28,
content: "test32",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
},
user_favorite: { #corresponding result from second query result
id: 1,
user_id: 2,
question_id: 88
}
},
{ #first query result
id: 87,
user_id: 18,
content: "testing riyas",
image: {
url: null,
thumb: {
url: null
},
mobile: {
url: null
}
},
user_favorite: {} #corresponding result from second query result if there is no result for particular question in favourite table
},
]
}
The model relationships are:
class Question
belongs_to :user
has_many :favourite
end
class Favourite
belongs_to :user
belongs_to :question
end
class User
has_many :questions
has_many :favourite
end
You should modify your jBuilder template to support nesting.Since your model association is like one question has_many favorite so it will be an array and you can easily nest one object inside another.
json.array! #questions do |question|
json.user_id question.user_id
json.content question.content
json.user_favorites question.favorites do |json,favorite|
json.id question.favorite.id
json.user_id question.favorite.user.id
json.question_id question.id
end
end
Here is a link that you can refer to for more clarity.
Generate a nested JSON array in JBuilder
Using JBuilder to create nested JSON output in rails
Hope it helps!.
You can add an association between user_favourite and question so that you can select all user favourites on one question.
Question.rb:
has_many :user_favourites
UserFavourite.rb:
belongs_to :question
Then, as your web action:
def index
#questions = Question.all.order('created_at DESC').page(params[:page]).per((ENV['ILM_QUESTIONS_PER_PAGE'] || 5).to_i)
end
And finally, in index.json.builder:
json.questions #questions do |question|
json.user_favourites question.user_favourites
end
including whatever other fields you want.

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