Hi I am building a Rails/Rack middleware for the Mailgun api, and doing a simple test for connection, which is fine in Postman with exact same params.
It is showing an error in test, Failure/Error: #app.call(env)
NoMethodError:
undefined method `merge!' for #<Mailgun::Tracking::MailLogger:0x0000557e6bc8bb50>
on running RSpec, and no merge method is attempted at all, so dont know where its come from. The api_key and endpoint are initialized in config/initialisers folder as per the Mailgun Tracking gem (which is being used) guidelines.
Only two other files used and code below.
MailLogger class under Mailgun::Tracking module, registered as middleware in app.
require 'logger'
require 'mailgun/tracking'
require 'rack/contrib'
require 'rack'
require 'byebug'
module Mailgun::Tracking
class MailLogger
def initialize(app, options = {
"HTTP_X_MAILGUN_API_KEY" => ENV['MAILGUN_API_KEY'],
"HTTP_X_MAILGUN_ENDPOINT" => ENV['MAILGUN_ENDPOINT']
})
#app = app
#options = options
end
def call(env)
#app.call(env)
end
end
end
The RSpec test
require 'logger'
require "json"
require "rails_helper"
Bundler.require(*Rails.groups)
require_relative "../../lib/middleware/mailgun/mailgun_tracking.rb"
RSpec.describe Mailgun::Tracking::MailLogger, type: 'Mailgun Webhook' do
subject(:response) { app.call(env) }
# env to pass in middleware, url endpoint & key
let(:app) { described_class.new(Rails.application) }
let(:env) { described_class.new(app, options = {
"HTTP_X_MAILGUN_API_KEY" => ENV['MAILGUN_API_KEY'],
"HTTP_X_MAILGUN_ENDPOINT" => ENV['MAILGUN_ENDPOINT']
}) }
it "returns a 200 status code" do
expect(response).to eq([200, {}, ["OK"]])
end
end
Am just looking for a 200, OK on connection, as I already get in Postman with the same header (api_key) and endpoint (events)
but throws this mysterious missing "merge" method error
Not encountered before.
Anyone know why?
Thanks
The actual code calling merge! is hidden in rack implementation and as such is filtered out from the error message. Your problem is that env you define in your test is not an instance of Rack::Environment but rather instance of your middleware. You can generate mocked environment instance using, for example, Rack::MockRequest.env_for("/", method: :get)
That being said, your unit test is currently testing the whole of the rack stack, including the routes and other middlewares. You can isolate your test better by using a mock application:
let(:app) { described_class.new(->(env) [200, {}, 'success']) }
Related
I am trying to setup feature testing on an application.
I decided to install Capybara, and thus added it to my project's Gemfile:
group :test do
gem "capybara"
end
I declare my tests on the spec/feature folder, and the test manages to execute:
require "rails_helper"
feature 'My Feature' do
scenario 'User visits feature page' do
visit '/my-feature'
expect(page).to have_text('Stuff')
end
end
Issue: I have an URL middleware that does not detect the env['REQUEST_URI] flag and thus my test fails:
class UrlNormalizationMiddleware
def initialize(app)
#app = app
end
def call(env)
uri_items = env['REQUEST_URI'].split('?')
...
#app.call(env)
end
end
The actual application loads, and passes values on env['REQUEST_URI'], but doesn't on the test environment.
Anything else that I need to setup?
Thanks!
REQUEST_URI is not part of the rack spec, which means it's not guaranteed to be set, and you shouldn't be using it in your middleware. Instead you should be using things like PATH_INFO, QUERY_STRING, etc. which are specified in the rack spec and should therefore be available - https://github.com/rack/rack/blob/master/SPEC
I have the following file struture in my Ruby-on-Rails project, for the specs:
/spec
/msd
/service
service_spec.rb
/support
/my_module
requests_stubs.rb
My request_stubs.rb have:
module MyModule::RequestsStubs
module_function
def list_clients
url = "dummysite.com/clients"
stub_request(:get, url).to_return(status: 200, body: "clients body")
end
end
In my service_spec.rb I have:
require 'rails_helper'
require 'support/my_module/requests_stubs'
...
because I only want the method available in this file.
The problem is that when running the tests I call the method MyModule::RequestsStubs.list_clients in the service_spec.rb file, I get the following error:
Failure/Error:
stub_request(:get, url).to_return(status: 200, body: "clients body")
NoMethodError:
undefined method `stub_request' for MyModule::RequestsStubs:Module
when accessing the WebMock method stub_request.
The WebMock gem is installed and is required in the spec_helper.rb file.
Why does the error occurs? It looks like it can't access the WebMock gem, or don't know how to access it. Any ideas on how to solve it?
stub_request is defined in the WebMock namespace, so you have to use WebMock.stub_request. To make it available globally, you need to add include WebMock::API to your rails_helper.
Even better, include webmock/rspec instead of just webmock -- it will take care of including the WebMock::API as well as setting up the webmock RSpec matchers.
I have a gem inside my Rails project, really simple, it translate an ip address to a real address (e.g. Gem.locate '127.0.0.1' returns { country: 'de' }
The code is like this
ip_locator/lib/service.rb
module IpLocator
class Service
include Singleton
attr_reader :db
def initialize
#db = MaxMindDB.new('db/GeoLite2-City.mmdb')
end
def locate(ip)
db.lookup ip
end
end
end
when I'm in the rails project, I can properly do this: IpLocator::Service.instance.locate '74.125.225.224' and get the information I need.
Now I'm trying add some tests to the gem (not in the rails project).
require 'spec_helper'
RSpec.describe IpLocator do
describe 'ip_locator' do
context 'when the data exists' do
let(:ip) { '74.125.225.224' }
subject do
IpLocator::Service.instance.locate ip
end
it 'return country data' do
expect(subject.found).to be_truthy
end
end
end
end
but when I run the test, I get
1) IpLocator ip_locator when the data exists return country data
Failure/Error: IpLocator::Service.instance.locate ip
NameError:
uninitialized constant IpLocator::Service
I already tried to change IpLocator::Service to Service, I tried require ip_locator inside the spec file (although it is already required inside spec_helper.rb). I'm pretty sure I'm doing something really stupid but not sure what exactly
Here is my spec_helper.rb
require 'bundler/setup'
Bundler.setup
require 'byebug'
require 'ip_locator'
Dir[File.dirname(__FILE__) + '/lib/*.rb'].each { |file| require file }
RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = '.rspec_status'
# Disable RSpec exposing methods globally on `Module` and `main`
config.disable_monkey_patching!
config.expect_with :rspec do |c|
c.syntax = :expect
end
end
and I'm seeing the error:
NameError: uninitialized constant IpLocator::Service::Singleton
Did you mean? SignalException
then, if I try to require singleton as well in the spec_helper, I get
1) IpLocator::Service ip_locator when the data exists return country data
Failure/Error: #db = ::MaxMindDB.new('db/GeoLite2-City.mmdb')
NameError:
uninitialized constant MaxMindDB
I'm getting crazy
I have a gem I'm working on that uses a railtie to add a middleware. Very simple stuff, followed the rails guides section almost exactly. Works fine in development/staging/production.
The middleware initializes a hash-like object in the env at a particular key.
But in my capybara tests, this key is only sometimes initialized. I added a debugger to the middleware and found that it isn't called every time I use the visit method.
What's more is that in this particular spec file, there are 4 examples, and each example calls visit once. But when I run the spec file, the middleware is sometimes called 3 times and sometimes called 2 times. Obviously the middleware stack should be called for every request.
tl;dr: sometimes calling visit in my capybara feature specs (with rack-test driver) does not result in my middleware stack being called.
Help?
ruby 2.0.0-p353
rails 4.0.2
capybara 2.2.1
rack-test 0.6.2
EDIT: This is some of the relevant code here: how the middleware is added and what it does. MyGem::Middleware#call is only sometimes called when using Capybara's visit method.
# railtie.rb
module MyGem
class Railtie < Rails::Railtie
initializer "my_gem.configure_rails_initialization" do |app|
app.middleware.use MyGem::Middleware
end
end
end
# middleware.rb
module MyGem
class Middleware
def initialize(app, options={})
#app = app
# options unused
end
def call(env)
# using a special internal version of the Rack::Session::Cookie class
session = MyGem::Rack::Session::Cookie.new(
#app,
:coder => MyGem::Rack::Session::Cookie::Base64::Marshal.new,
:key => ENV_SESSION_KEY,
:path => '/',
:domain => domain(env),
:expire_after => 6.weeks.to_i, # seconds till this expires
:secret => 'my_gem_secret_14f1c4ad25a6be00fe53f5fd2d746167',
)
# use Rack::Session:Cookie method
session.context(env, #app)
end
end
end
Figured it out!
I was also adding a Warden hook that expected the env key to be added after signing users in and out, and if the Warden::Manager middleware was added prior to my gem's middleware, then it would error out when running my hook that expected that env key to be set.
Solution was to do this in my railtie:
app.middleware.insert_before Warden::Manager, MyGem::Middleware
I have part of my rails 2 webservice application which is used as SOAP service (historical reasons, rest of app is REST).
Just two operations AddLead and ShowLead, with wsdl on /soap/wsdl.
I want to test this operations by Rspec integrations tests.
Trying to use Savon gem (/spec/integration/soap_spec.rb):
require "spec_helper"
require 'rubygems'
require 'savon'
describe "Leads" do
before(:all) do
wsdl= "http://localhost:3000/soap/wsdl"
wsdl = "http://www.example.com/soap/wsdl"
#client = Savon.client(:wsdl => wsdl )
puts("WSDL actions: #{#client.operations}")
end
end
But I can not find which URL I should use to point to WSDL.
URL localhost:3000 does not work, ending with error:
Errno::ECONNREFUSED in 'Leads before(:all)'
Connection could not be made, because target server it actively denied. - connect(2)
URL www.example.com (which is output from test url helpers) does not work either, ending with error:
Wasabi::Resolver::HTTPError in 'Leads before(:all)'
Error: 302
Any ideas?
Foton
Try the following link: http://blog.johnsonch.com/2013/04/18/rails-3-soap-and-testing-oh-my/
Inside of your describe block use the HTTPI rack adapter, and then configure that adapter to mount your application. This will give you the ability to use a specific url.
require 'spec_helper'
require 'savon'
describe API::MyService do
HTTPI.adapter = :rack
HTTPI::Adapter::Rack.mount 'application', MyApp::Application
it 'can get a response' do
application_base = "http://application"
client = Savon::Client.new({:wsdl => application_base + '/soap/wsdl' })
...
...
end
...