Rspec send_data test not passing - ruby-on-rails

I can't seem to get this test to pass and I don't understand why.
controller_spec.rb:
require 'rails_helper'
RSpec.describe QuotationRequestsController, type: :controller do
describe "GET download" do
it "streams the sample text as a text file" do
#setup
quotation_request = create(:quotation_request)
file_options = {filename: "#{quotation_request.id}-#{quotation_request.client.name.parameterize}.txt", type: 'plain/text', disposition: 'attachment'}
#exercise
get :download, id: quotation_request
#verification
expect(#controller).to receive(:send_data).with(file_options) {#controller.render nothing: true}
end
end
end
controller:
def download
#quotation_request = QuotationRequest.find(params[:id])
send_data #quotation_request.sample_text, {
filename: #quotation_request.sample_text_file,
type: "text/plain",
disposition: "attachment"
}
end
Output of test:
1) QuotationRequestsController GET download streams the sample text as a text file
Failure/Error: expect(#controller).to receive(:send_data).with(file_options) {
#controller.render nothing: true
}
(# <QuotationRequestsController:0x007ff35f926058>).send_data({
:filename=>"1-peter-johnson.txt",
:type=>"plain/text",
:disposition=>"attachment"
})
expected: 1 time with arguments: ({
:filename=>"1-peter-johnson.txt",
:type=>"plain/text", :disposition=>"attachment"
})
received: 0 times
# ./spec/controllers/quotation_requests_controller_spec.rb:380:in `block (3 levels) in <top (required)>'
# -e:1:in `<main>'

You should pass 2 arguments
expect(#controller).to receive(:send_data).with(quotation_request.sample_text, file_options) {#controller.render nothing: true}

#exercise
get :download, id: quotation_request
#verification
expect(#controller).to receive(:send_data).with(file_options) {#controller.render nothing: true}
This is backwards. The expectation should come before the method call.

You write following:
Get file
Mock file
But right case is inverted:
Mock file
Get file
try following (using before):
require 'rails_helper'
RSpec.describe QuotationRequestsController, type: :controller do
describe "GET download" do
let(:quotation_request) { create(:quotation_request) }
let(:file_options) { {filename: "#{quotation_request.id}-#{quotation_request.client.name.parameterize}.txt", type: 'plain/text', disposition: 'attachment'} }
before do
expect(#controller).to receive(:send_data)
.with(file_options) { #controller.render nothing: true }
end
it "streams the sample text as a text file" do
get :download, id: quotation_request
end
end
end

Rails 5:
expect(#controller).to receive(:send_data).with(quotation_request.sample_text, file_options) {#controller.head :ok}
Ref: https://stackoverflow.com/a/43428992

Related

Rails request spec - undefined method `body' for nil:NilClass (rswag)

I'm trying to generate API documentation with rwsag, and I'm confused about a couple of my specs failing.
Here is my request spec:-
require 'swagger_helper' ### this includes 'rails_helper'
RSpec.describe 'api/v1/users', type: :request do
before(:each) { host! 'localhost:3001' }
path '/api/v1/users' do
post('create user') do
tags 'users'
consumes 'application/json'
parameter name: :user, in: :body, schema: {
type: :object,
properties: {
title: { type: :string },
description: { type: :string },
date: { type: :datetime },
budget: { type: :decimal },
awarded: { type: :boolean }
},
required: [ 'title', 'description' ]
}
response(200, 'successful') do
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
run_test!
end
end
end
Here are my two errors which don't make much sense to me:-
1) api/v1/users /api/v1/users post successful returns a 200 response
Got 0 failures and 2 other errors:
1.1) Failure/Error: super
NoMethodError:
undefined method `user' for #<RSpec::ExampleGroups::ApiV1Users::ApiV1Users::Post::Successful:0x0000aaaad7d06600>
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:197:in `build_json_payload'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:180:in `add_payload'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:22:in `block in build_request'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:18:in `tap'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:18:in `build_request'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/example_helpers.rb:10:in `submit_request'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/example_group_helpers.rb:94:in `block in run_test!'
1.2) Failure/Error: example: JSON.parse(response.body, symbolize_names: true)
NoMethodError:
undefined method `body' for nil:NilClass
# ./spec/requests/api/v1/users_spec.rb:84:in `block (5 levels) in <main>'
And my standard controller action:-
module Api
module V1
class UsersController < ApplicationController
# POST /api/v1/users
def create
#user = Job.new(user_params)
if #user.save
render json: #user, status: :created
else
render json: #user.errors, status: :unprocessable_entity
end
end
Any idea what is causing this error? I'm also a little confused with the generated rswag spec structure - i.e. no expects anywhere.
The answer is that I was missing the below let(:user) line:-
response(201, 'successful') do
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
let(:user) { { title: 'foo', description: 'bar' } }
run_test!
end

Rspec for Shopify controllers? Stuck on a 302 for authentication

My project is a Rails 5.2 app, running Ruby 2.6, and uses the shopify_gem and factory_bot_rails.
I have a controller that inherits from ShopifyController. My unit tests for controllers are stuck at a 302. I'm unable to figure out how to get past authentication...
I've tried these tutorials and other links, but no luck:
http://www.codeshopify.com/blog_posts/testing-shopify-authenticated-controllers-with-rspec-rails
https://community.shopify.com/c/Shopify-APIs-SDKs/Testing-a-Rails-app-created-through-shopify-app-gem/td-p/337337
https://github.com/Shopify/shopify_app/issues/445
https://github.com/Shopify/shopify_app/issues/731
My controller test is below
require 'rails_helper'
describe OnboardingController, type: :controller do
before do
shop = FactoryBot.create(:shop)
request.env['rack.url_scheme'] = 'https'
#request.session[:shopify] = shop.id
#request.session[:shopify_domain] = shop.shopify_domain
end
it 'onboards correctly', :focus do
get :onboard_completed
expect(response).to have_http_status(:success)
end
end
I was also playing with this code, but it failed (errors in comments):
module ShopifyHelper
def login(shop)
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:shopify,
provider: 'shopify',
uid: shop.shopify_domain,
credentials: { token: shop.shopify_token })
Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[:shopify]
get "/auth/shopify" # this leads to a UrlGenerationError
follow_redirect! # this is an undefined method. Seems to be a MiniTest thing
end
end
require 'rails_helper'
RSpec.describe "Home", type: :request do
def login(shop)
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:shopify,
provider: 'shopify',
uid: shop.shopify_domain,
credentials: { token: shop.shopify_token })
Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[:shopify]
get "/auth/shopify"
follow_redirect!
#request.session[:shopify] = shop.id
#request.session[:shopify_domain] = shop.shopify_domain
end
describe "GET /" do
it "works!" do
shop = Shop.first || create(:shop)
login(shop)
get root_path
shop.with_shopify!
expect(assigns(:products)).to eq ShopifyAPI::Product.find(:all, params: { limit: 10 })
expect(response).to render_template(:index)
expect(response).to have_http_status(200)
end
end
end
Something like this works for me, your getting the errors in your function probably because you do not have get and follow_redirect! functions defined in your ShopifyHelper module context.
Reference: http://www.codeshopify.com/blog_posts/testing-shopify-authenticated-controllers-with-rspec-rails
This ended up being the working solution
require 'rails_helper'
describe WizardController, type: :controller do
before do
shop = FactoryBot.create(:shop)
request.env['rack.url_scheme'] = 'https'
allow(shop).to receive(:wizard_completed?).and_return(false)
allow(Shop).to receive(:current_shop).and_return(shop)
# #note: my original code had "session[:shopify]" of "session[:shop]", which was the error
session[:shop_id] = shop.id
session[:shopify_domain] = shop.shopify_domain
end
it 'enter test here', :focus do
get :wizard
expect(response).to have_http_status(:success)
end
end
This worked for me:
# File: spec/support/request_helper.rb
def shopify_login(shop)
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:shopify, provider: 'shopify', uid: shop.myshopify_domain,
credentials: { token: shop.api_token })
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:shopify]
get "/auth/shopify/callback?shop=#{shop.myshopify_domain}"
follow_redirect!
#request.session[:shopify] = shop.shopify_id
#request.session[:shop_id] = shop.id
#request.session[:shopify_domain] = shop.myshopify_domain
end
Btw, testing controllers are deprecated in favour of requests.
RSpec.describe 'ShopsController', type: :request do
let(:shop) { FactoryBot.build :shop }
let(:plan) { FactoryBot.build :enterprise_plan }
let(:subscription) { FactoryBot.create :subscription, shop: shop, plan: plan }
describe 'GET#product_search' do
it 'returns a successful 200 response for listing action do' do
VCR.use_cassette('shop-search-product', record: :new_episodes) do
new_subscrip = subscription
shopify_login(new_subscrip.shop)
get product_search_path, { params: { query: 'bike' } }
json = JSON.parse(response.body)
expect(response).to be_successful
expect(json.length).to eq(7)
end
end
end
Remember to setup "admin { true }" in your shop's FactoryBot if you are using the 'shopify_app' gem.

How to test wash_out controller with Rspec in Rails 5

I am using wash_out to create a soap webservice, this is my controller:
class SignerController < ApplicationController
soap_service
soap_action 'xmlSign', args: {
xml: :string,
certificate: :string
},
return: {
signed_xml: :string,
error: :string
},
to: :xml_sign
def xml_sign
render soap: {
signed_xml: "TODO"
}
rescue
render soap: {
error: $!.message
}
end
end
following http://blog.johnsonch.com/2013/04/18/rails-3-soap-and-testing-oh-my/ I wrote this test:
require 'rails_helper'
require 'savon'
RSpec.describe SignerController, type: :controller do
HTTPI.adapter = :rack
HTTPI::Adapter::Rack.mount 'app', Signer::Application
it 'signs an xml' do
application_base = "http://app"
client = Savon::Client.new({:wsdl => application_base + signer_wsdl_path })
result = client.call(:xmlSign, xml: 'xml', certificate: 'cert')
result.body[:xml_sign][:value].should_not be_nil
end
end
but rspec always throws this error:
NoMethodError:
undefined method `element_children' for nil:NilClass
# ./spec/controllers/signer_controller_spec.rb:12:in `block (2 levels) in <top (required)>'
I am using rails 5.1.5, wash_out 0.12, httpi 2.4.2 and savon 2.11.2
how can I fix this?
What worked for me was adding "render_views" to the controller spec. For example
RSpec.describe SoapController, type: controller do
render_views # added render_views
HTTPI.adapter = :rack
HTTPI::Adapter::Rack.mount 'application', PhoneDialogue::Application
it 'signs an xml' do
application_base = "http://app"
client = Savon::Client.new({:wsdl => application_base + soap_wsdl_path })
result = client.call(:xmlSign, xml: 'xml', certificate: 'cert')
result.body[:xml_sign][:value].should_not be_nil
end
end

Where to put mocks

I'm trying to spec this action.
def get
#asset = current_user.assets.find(params[:id])
send_file #asset.uploaded_file.path, type: #asset.uploaded_file_content_type
rescue ActionController::MissingFile
redirect_to assets_url, error: 'missing file'
end
To test the send file method we mock it out.
controller.should_receive(:send_file)
However, I have no idea where to put this mock:
Here's how my spec looks:
subject { response }
let!(:user) { FactoryGirl.create(:user) }
let!(:user_2) { FactoryGirl.create(:user) }
let!(:asset) { FactoryGirl.create(:asset, user_id: user.id) }
let!(:file) { fixture_file_upload('files/eve.jpg', 'image/jpeg') }
let!(:folder) { FactoryGirl.create(:folder, user_id: user.id, parent_id: nil) }
before do
sign_in user
end
describe '#get' do
context 'when exists' do
before do
get :get, id: asset.id
end
# controller.should_receive(:send_file).with(*args) <-- I need to test that
it { should have_http_status 302 }
end
context 'when doesn\'t exist' do
before do
get :get, id: 765
end
it { should redirect_to_location '/assets'}
it { should set_flash_type_to :error }
it { should set_flash_message_to 'missing file' }
end
end
How do I test line 6. I want to keep the one line syntax if possible.
Put it in the before block
before do
controller.should_receive(:send_file)
get :get, id: asset.id
end

How to test send_data in RSpec? Or...what should I be testing for in this case?

What's the best way to test the following code using RSpec? And what should I be testing for? The show action opens a file and streams it. Also, if the action relies on a file existing somewhere, can I test that?
def show
image_option = params[:image_option]
respond_to do |format|
format.js
format.pdf {open_bmap_file("#{#bmap.bmap_pdf_file}", 'application/pdf', "#{#bmap.bmap_name}.pdf", "pdf", "pdf")}
format.png {open_bmap_file("#{#bmap.bmap_png_file}", 'image/png', "#{#bmap.bmap_name}.png", "png", image_option)}
end
end
private
def open_bmap_file(filename, application_type, send_filename, format, image_option = nil)
filename = "app/assets/images/image_not_available_small.png" unless File.exist? filename
path = Bmap.bmaps_pngs_path
case image_option
when "image"
filename = "#{#bmap.bmap_name}.png"
when "large_thumbnail"
filename = "#{#bmap.bmap_name}_large_thumb.png"
when "thumbnail"
filename = "#{#bmap.bmap_name}_thumb.png"
when "pdf"
filename = "#{#bmap.bmap_name}.pdf"
path = Bmap.bmaps_pdfs_path
else
filename = "#{#bmap.bmap_name}.pdf"
path = Bmap.bmaps_pdfs_path
end
begin
File.open(path + filename, 'rb') do |f|
send_data f.read, :disposition => image_option == "pdf" ? 'attachment' : 'inline', :type => application_type, :filename => send_filename
end
rescue
flash[:error] = 'File not found.'
redirect_to root_url
end
I needed to test send_data in a controller action that downloads a csv file, and I did it in the following way.
controller:
def index
respond_to do |format|
format.csv do
send_data(Model.generate_csv,
type: 'text/csv; charset=utf-8; header=present',
filename: "report.csv",
disposition: 'attachment')
end
end
end
(rspec 2 solution) controller_spec:
context "when format is csv" do
let(:csv_string) { Model.generate_csv }
let(:csv_options) { {filename: "report.csv", disposition: 'attachment', type: 'text/csv; charset=utf-8; header=present'} }
it "should return a csv attachment" do
#controller.should_receive(:send_data).with(csv_string, csv_options).
and_return { #controller.render nothing: true } # to prevent a 'missing template' error
get :index, format: :csv
end
end
(rspec 3 solution) controller_spec:
context "when format is csv" do
let(:csv_string) { Model.generate_csv }
let(:csv_options) { {filename: "report.csv", disposition: 'attachment', type: 'text/csv; charset=utf-8; header=present'} }
it "should return a csv attachment" do
expect(#controller).to receive(:send_data).with(csv_string, csv_options) {
#controller.render nothing: true # to prevent a 'missing template' error
}
get :index, format: :csv
end
end
It's helped me
stub(controller).send_data { controller.render nothing: true }

Resources