How to write rspec for link shortener? - ruby-on-rails

This is a method in my model
def event_share_url(destination: nil)
link_options = {
url: self.learnmore_url.present? ? learnmore_url : event_url(self.id),
destination: destination || "other",
asset: self,
user: Current.user
}
link = Link.fetch_or_create_link link_options
share_url = link.share_link
end
How to write rspec for this model method ?
def share_link
my_link = self._share_link
link_shortener_client = LinkShortener.new({ source_url: my_link })
end
Here is the method in link.rb with link shortener

I suggest you to test LinkShortener separately and then test that your method call the service.
# Assuming you've tested separately LinkShortener
describe '#share_link' do
subject { model.share_link }
let(:model} { create(:your_factory) }
let(:foo) { :bar }
it 'calls correct service' do
expect(LinkShortener).to receive(:new).with({source_url: model._share_link})
.and_return(foo)
expect(subject).to eq(:bar)
end
end

Related

Rspec ignore allow block?

I want to test a rails job which call an endpoint of hubspot API (GET /crm/v3/owners/{ownerId}) and update a record with infos of the request result.
The problem is that I use this gem as an API wrapper and my before block seems like ignored because the result shows that the API call can't give me an owner object with this owner_id (the one given as parameter of attributes is obviously fake). A before block is supposed to override the "normal" response of the controller isn't it ?
I really don't know what I am doing wrong ..
For more context:
My job code
module Hubspots
module Contracts
class UpdateJob < BaseJob
queue_as :high_priority
def perform(attributes)
contract = Contract.find_by(hubspot_sales_deal_id: attributes[:hubspot_sales_deal_id])
return if contract.nil?
deal_owner = client.crm.owners.owners_api.get_by_id(owner_id: attributes[:hubspot_tailor_deal_owner],
id_property: 'id', archived: false)
attributes[:hubspot_tailor_deal_owner] = get_owner_name(deal_owner)
contract.update!(attributes)
end
private
def get_owner_name(hubspot_owner_object)
"#{hubspot_owner_object.last_name.upcase} #{hubspot_owner_object.first_name.capitalize}"
end
end
end
end
My test code
RSpec.describe Hubspots::Contracts::UpdateJob, type: :job do
let!(:job) { described_class.new }
let(:perform) { job.perform(attributes) }
let!(:contract) { create(:contract, hubspot_sales_deal_id: 123) }
let!(:attributes) do
{ hubspot_tailor_deal_id: 456, hubspot_tailor_deal_owner: 876, hubspot_sales_deal_id: 123 }
end
let!(:deal_owner_api) { Hubspot::Client.new(access_token: ENV['HUBSPOT_ACCESS_TOKEN']).crm.owners.owners_api }
let!(:deal_owner_properties) { { last_name: 'Doe', first_name: 'John' } }
before do
allow(deal_owner_api).to receive(:get_by_id).and_return(deal_owner_properties)
end
describe '#perform' do
it 'updates contract' do
expect { perform }.to change { contract.reload.hubspot_tailor_deal_owner }.from(nil)
.to('DOE John')
end
end
end
Test result
I try to rtfm on google but I didn't find the solution yet (I'm always bad for rtfm btw)
Ok my CTO finaly gave me the solution by using the Webmock gem
The code snippet :
before do
stub_request(:get, 'https://api.hubapi.com/crm/v3/owners/876?archived=false&idProperty=id')
.with(headers: { 'Authorization' => "Bearer #{ENV['HUBSPOT_ACCESS_TOKEN']}" }).to_return(status: 200, body: {
firstName: 'John',
lastName: 'Doe'
}.to_json, headers: {})
end
describe '#perform' do
it 'updates contract' do
expect { perform }.to change { contract.reload.hubspot_tailor_deal_owner }.from(nil)
.to('DOE John')
end
end

RSpec for stubbed method inside call

I'm trying test class which is responsible for creating Jira tickets. I want to stub create_issue method which is inside of method call
module JiraTickets
class Creator
def initialize(webhook)
#webhook = webhook
end
def call
create_issue(support_ticket_class, webhook)
end
private
def client
#client ||= JIRA::Client.new
end
def support_ticket_class
#support_ticket_class ||= "SupportBoard::Issues::#{webhook.action_type_class}".constantize
end
def create_issue(support_ticket_class, webhook)
issue = client.Issue.build
issue.save(support_ticket_class.new(webhook).call)
end
def fields
{
'fields' => {
'summary' => 'example.rb',
'project' => { 'id' => '11' },
'issuetype' => { 'id' => '3' }
}
}
end
end
end
The create_issue method should return true. So I've made a specs:
RSpec.describe JiraTickets::Creator do
describe '#call' do
subject { described_class.new(webhook).call }
let(:webhook) { GithubApi::Webhook.new(webhook_hash, 'repository') }
let(:webhook_hash) { { repository: { name: 'Test-repo' }, action: 'publicized' } }
let(:creator_instance) { instance_double(JiraTickets::Creator) }
before do
allow(described_class).to receive(:new).with(webhook).and_return(creator_instance)
allow(creator_instance).to receive(:call).and_return(true)
end
context 'when webhook class is supported' do
it 'expect to create Jira ticket' do
expect(subject).to receive(:call)
end
end
end
end
But I'm getting an error:
Failure/Error: expect(subject).to receive(:call)
true does not implement: call
You just need to check that the method was called on the stub creator_instance
RSpec.describe JiraTickets::Creator do
describe '#call' do
subject { described_class.new(webhook) }
let(:webhook) { GithubApi::Webhook.new(webhook_hash, 'repository') }
let(:webhook_hash) { { repository: { name: 'Test-repo' }, action: 'publicized' } }
before do
allow_any_instance_of(described_class).to receive(:create_issue).with(any_args).and_return(true)
end
context 'when webhook class is supported' do
it 'expects to create Jira ticket' do
expect(subject.call).to eq(true)
end
end
end
end

Rails RSpec controller test changes second attribute

I have a controller called "CrossLayerParamtersController". If one specified attribute (donor_layer) is updated. I want the attribute (donor_material) to be set to "0".
In the controller update method I'm checking if the donor_layer params are present and if they are the donor_material is set to 0:
Controller file:
cross_layer_parameter_controller.rb
def update
#stack = Stack.find(params[:stack_id])
#cross_layer_parameter = CrossLayerParameter.find(params[:id])
if params[:cross_layer_parameter][:donor_layer]
#cross_layer_parameter.donor_material = 0
end
respond_to do |format|
if #cross_layer_parameter.update(cross_layer_parameter_params)
new_rows = render_to_string('stacks/_cross_layer_parameters.html.erb', layout: false, locals: { stack: #stack} )
id = #cross_layer_parameter.id
format.html { redirect_to(#stack) }
format.json { render json: { new_rows: new_rows, id: id, status: 200 } }
else
format.html { redirect_to edit_stack_path(#cross_layer_parameter) }
format.json { respond_with_bip(#cross_layer_parameter) }
end
end
end
This is working as expected and I want to write an RSpec test in my controller tests that checks that. Until now I have:
RSpec test file:
cross_layer_parameter_controller_spec.rb
describe 'PUT #update' do
context "with params donor_layer or acceptor_layer" do
before do
post :update, params: { stack_id: #stack.id, donor_layer: 5, id: #cross_layer_parameter.id, cross_layer_parameter: FactoryGirl.attributes_for(:cross_layer_parameter) }
#cross_layer_parameter.reload
end
it "should changed the donor material '0'" do
expect(#cross_layer_parameter.donor_material).to eq 0
end
end
end
I suspect FactoryGirl is messing it up but I'm not sure. Is there another way to test wittout using FactoryGirl? I've tried the put underneath but this didn't work.
post :update, params: { stack_id: #stack.id, donor_layer: 5, id: #cross_layer_parameter.id }
FactoryGirl file:
cross_layer_parameter.rb
require 'faker'
FactoryGirl.define do
factory :cross_layer_parameter do
donor_layer { Faker::Number.between(0, 10) }
donor_material { Faker::Number.between(0, 10) }
acceptor_layer { Faker::Number.between(0, 10) }
acceptor_material { Faker::Number.between(0, 10) }
interaction { Faker::Number.between(1, 9) }
value { Faker::Number.decimal(2) }
end
end
You should not add params as params argument there. First part is type of action and action (put and update) and rest are params. Here is code sample for put update:
let(:params) { { attachment: '' } }
let(:action) { put :update, parent_id: parent.id, id: object.id, object: params }

Rspec - How to test if mailer is using proper template

There is a lot info about how to test mailers.
But I haven't found any resources how to test mailer to check if they REALLY use correct template.
example:
class NewsletterMailer < ActionMailer::Base
include SendGrid
default from: -> { SystemConfiguration.newsletter_from_email }
def send_newsletter_to_groups(newsletter_campaign_id, group_ids)
newsletter_campaign = NewsletterCampaign.find newsletter_campaign_id
emails = Group.where(:id => group_ids).map(&:emails).flatten
build_and_send_email(newsletter_campaign, emails)
end
end
on app/views/newsletter_mailer/send_newsletter_to_group.html.erb I have typo.
I wrote send_newsletter_to_group.html.erb instead of send_newsletter_to_groups.html.erb
My spec still pass:
require "spec_helper"
describe NewsletterMailer do
before { create(:system_configuration) }
let(:newsletter) { create(:newsletter_campaign) }
describe '.send_newsletter_to_groups' do
before do
create(:system_configuration)
create_list(:group, 3)
create_list(:user, 2, groups: [Group.first], newsletter_subscription: true)
create_list(:user, 2, groups: [Group.last], newsletter_subscription: true)
create_list(:user, 2, name: "pippo")
end
let(:group_ids) { Group.pluck(:id) }
subject { NewsletterMailer.send_newsletter_to_groups(newsletter.id, group_ids) }
its(:to) { should == User.where("name != 'pippo'").map(&:email) }
its(:from) { should be_present }
its(:subject) { should be_present }
end
end
But email doesn't contain body.
It just send blank email, cuz at the name of the partial (send_newsletter_to_group.html.erb) I got typo.
How to test this? In Mailer.
I use email spec for this.
it "should contain the user's message in the mail body" do
#email.should have_body_text(/Jojo Binks/)
end
Just look for some text that you know is part of the template.

How to stub after_create callback save! in model?

I receive following error:
Output:
1) LabelsController#create label is new creates a new label
Failure/Error: post :create, attributes[:label], format: :json
NoMethodError:
undefined method `save!' for nil:NilClass
# ./app/models/labeling.rb:17:in `update_target'
In Labeling model:
after_create :update_target
def update_target
self.target.save!
end
Test:
require 'spec_helper'
describe LabelsController do
before(:each) do
controller.stub(:current_user).and_return(mock_model(User))
stub_request(:any, "www.example.com").to_return(status: 200)
end
describe "#create" do
context "label is new" do
it "creates a new label" do
attributes = {
label: {
name: "test",
labeling: {
target_type: "Link", target_id: 1
}
}
}
response.status.should == 200
post :create, attributes[:label], format: :json
end
end
end
end
Labeling controller:
def create
label = Label.find_by_name(params[:name])
labeling = label.labelings.build do |lb|
lb.user_id = current_user.id
lb.target_type = params[:labeling][:target_type]
lb.target_id = params[:labeling][:target_id]
end
if labeling.save
render json: {
name: label.name,
id: label.id,
labeling: {
id: labeling.id
}
}
end
end
By the looks of it you don't have a Target with ID 1 on the database, so where you refer to self.target the returned value is nil.
What I'd do in your case is first create a target and then pass its id to the attributes hash:
target = Traget.create!
attributes = {
label: {
name: "test",
labeling: {
target_type: "Link", target_id: target.id
}
}
}
This way you don't need to stub anything.
If you really must stub the method you can use RSpecs any_instance method:
Labeling.any_instance.stub(:update_target).and_return(true)

Resources