How to test wash_out controller with Rspec in Rails 5 - ruby-on-rails

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

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.

Rspec send_data test not passing

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

How do you test authentication in API controllers

I'm writing a Rails API and am stuck trying to write controllers that will test the authentication. For instance, I have in my PostController before_action :authenticate which is
def authenticate
authenticate_or_request_with_http_token do |token, options|
User.find_by(:auth_token => token)
end
end
And this is my PostsController test:
require 'rails_helper'
RSpec.describe Api::PostsController, type: :controller do
let(:valid_attributes) {
{
"title" => "Post title",
"content" => "Post content",
"status" => "published"
}
}
let(:invalid_attributes) {
{
"title" => "",
"content" => "",
"status" => ""
}
}
let(:valid_session) { {} }
before do
params = { session: { email: 'wagner.matos#mac.com', password: 'foobar' } }
SessionsController.create params
#post = Post.new({
title: "Some title",
content: 'Some content',
status: "published"
})
end
it "creates a new post" do
post :create, post: #post
expect(Post.count).to eq(1)
end
end
Yet the above is failing with the following error:
1) Api::PostsController creates a new post
Failure/Error: SessionsController.create params
NoMethodError:
undefined method `create' for SessionsController:Class
Any suggestions?
You can not invoke create action of SessionsController with class. Better you create user object and assign to request.env the same token. like below sample code -
require 'rails_helper'
RSpec.describe Api::PostsController, type: :controller do
before do
user = User.create( :auth_token => 'token' )
request.env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Token.encode_credentials("token")
end
end

Rspec Stubbing Object.map()

In my rails controller, I am trying to write a test for the method search_backups. The issue I am having is that devices_ids_from_elastic = ConfigTextSearch.search search_term returns nothing and so the test runs .map on devices_ids_from_elastic which is incorrectly a #<String:0x007f8a90b67ca0>.
How can I stub out devices_ids_from_elastic.map() to fix this issue?
Failures:
1) ReportsController access control allows architects to search backups
Failure/Error: post 'search_backups'
NoMethodError:
undefined method `each_pair' for #<String:0x007f8a90b67ca0>
# ./app/controllers/reports_controller.rb:19:in `map'
# ./app/controllers/reports_controller.rb:19:in `elastic_mongo_lookup'
# ./app/controllers/reports_controller.rb:32:in `search_backups'
# ./spec/controllers/reports_controller_spec.rb:123:in `block (3 levels) in <top (required)>'
test:
describe "controller method test" do
before do
allow(CSV).to receive(:generate).and_return("1234, blah")
stub_request(:get, "http://localhost:9200/mongo_index/config_files/_search?q=").
with(:headers => {'Expect'=>'', 'User-Agent'=>'Faraday v0.9.1'}).
to_return(:status => 200, :body => '{lots of json stuff in here }', :headers => {})
it "allows users to search backup log files" do
reports = double(ReportsController)
reports.stub(:map).and_return("help")
post 'search_backups'
end
controller:
search_backups
def elastic_mongo_lookup(search_term)
devices_ids_from_elastic = ConfigTextSearch.search search_term
device_ids = devices_ids_from_elastic.map { |device| device._source.device_id }
csv_string = CSV.generate do |csv|
Device.where(:_id.in => device_ids).each do |device|
csv << [device.logical_name, device.primary_ip]
end
end
return csv_string
end
def search_backups
authorize! :read, :custom_report
csv_string = elastic_mongo_lookup params[:search_term]
if csv_string.blank?
flash[:notice] = "No results were found"
redirect_to reports_path
else
render text: "DeviceID, primary_ip\n" + csv_string
end
end#search_backups
Try this: allow(ConfigTextSearch).to receive(:search).with({}).and_return([])

Resources