UPDATE: The following appears to be a result of the Rails environment not being loaded when rake test runs my unit tests (currently only a single module which lives in /lib). Is it possible to include the environment from the command line, instead of changing the rake task itself? Is there a better way to go about this (short of using dependency injection in order to introduce Rails.logger, which is what I'll probably end up doing)?
I'm seeing a NoMethodError in my test environment because of the following call to Rails.logger.error:
# /lib/square.rb
module Square
def self.get_total_sales_for_location(location_id, timeout: 5)
begin
url = "https://connect.squareup.com/v2/locations/#{location_id}/transactions"
headers = {Authorization: "Bearer #{ENV['SQUARE_PERSONAL_ACCESS_TOKEN']}"}
response = RestClient::Request.execute headers: headers,
method: :get,
timeout: timeout,
url: url
if response.code == 200
JSON.parse response.body
else
fail
end
rescue => error
Rails.logger.error "Square::get_total_sales_for_location - #{error.message}"
raise
end
end
end
This all works fine from the console, however running my tests results in: NoMethodError: undefined method 'error' for nil:NilClass
My temporary workaround is to use :try to prevent the exception, but that's very kludgy.
You can do rake environment test. But I would guess it would be nicer to just make the test task dependent on the environment with task test: :environment do
Related
When debugging failing integration tests, I keep running into the same problem where the exceptions raised in my code are suppressed and not shown in the testing output.
For example, for the following controller and test:
class RegistrationController::ApplicationController
def create
# some code that raises an exception
end
end
class RegistrationFlowTest < ActionDispatch::IntegrationTest
test 'user registers successfully' do
post sign_up_path, params: { username: 'arnold', password: '123' }
assert_response :success
end
end
The output is something like
Minitest::Assertion: Expected response to be a <2XX: success>, but was a <500: Internal Server Error>
Is there a way to see the exact raised exception? Instead of just the difference of HTTP response code?
Thanks!
Simon
My recommended approach to this issue would be to actually parse the response provided by Rails (at least by default in test and development environments) which includes the stacktrace for the error and handle that in the case that your test fails. This has the advantage that it won't output the stacktrace when errors are raised that don't result in failing tests (e.g. scenarios where you are intentionally testing how failures are handled).
This little module that I've crafted will allow you to call assert_response_with_errors to assert the response to a call but output the exception message and stack trace in a readable format when the response is not what you expected.
module ActionDispatch
module Assertions
module CustomResponseAssertions
# Use this method when you want to assert a response body but also print the exception
# and full stack trace to the test console.
# Useful when you are getting errors in integration tests but don't know what they are.
#
# Example:
# user_session.post create_gene_path, params: {...}
# user_session.assert_response_with_errors :created
def assert_response_with_errors(type, message = nil)
assert_response(type, message)
rescue Minitest::Assertion => e
message = e.message
message += "\nException message: #{#response.parsed_body[:exception]}"
stack_trace = #response.parsed_body[:traces][:'Application Trace'].map { |line| line[:trace] }.join "\n"
message += "\nException stack trace start"
message += "\n#{stack_trace}"
message += "\nException stack trace end"
raise Minitest::Assertion, message
end
end
end
end
To use this, you need to include it into ActionDispatch::Assertions before Rails has loaded its stack in your test_helper.rb. So just prepend the include into your test_helper.rb, like this:
ActionDispatch::Assertions.include ActionDispatch::Assertions::CustomResponseAssertions
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
...
This will happen because Rails controllers by default handle exceptions and raise the 500 status, making the exceptions invisible to the test suite (which is very helpful if errors are raised in unit tests of a model). Options for disabling this in your test suite, or alternative workarounds, are discussed here.
The key lines of code from that link, which should be added to test/integration/integration_test_helper.rb:
ActionController::Base.class_eval do
def perform_action
perform_action_without_rescue
end
end
Dispatcher.class_eval do
def self.failsafe_response(output, status, exception = nil)
raise exception
end
end
EDIT: I've noticed that that link is quite old now. I'm really familiar with Rack, so whilst the first block looks ok to me, I'm not sure if the second will still be current. You might need to have a look at the relevant current Rails guide if it needs bringing up to date.
So in the old plugin for Rails 2 there used to be a method called notifiable that I could use to surround whatever Rake task I needed to attach exception notifier to. However, when I try to run my rake task it gives me an undefined method error. I looked around and noticed someone else use the exception_notify method and tried replacing this:
task(:create_orders_for => :environment) do
notifiable do
...
end
end
with this:
exception_notify {:create_orders_for => :environment} do
#notifiable do
...
end
But it doesn't work. Does anyone know what the Rails 3 version of this method is? I can't find it anywhere.
So this is what I eventually ended up doing. Works great.
Add the middleware configuration to your environment/whatever_environment_you_want.rb file
If you're testing in dev or test, you need to set the consider_all_requests_local to false
Change your rake task to this:
task(:create_orders_for => :environment) do
begin
...
rescue => e
ExceptionNotifier::Notifier.exception_notification(Rails.env, e).deliver
end
end
I am using rescue to handle all the heavy lifting background tasks,
In my library/parsers/file.rb I have
Resque.enqueue(Hello)
This will redirect app/workers/file.rb where I have
class Hello
def self.perform(page)
.......
.......
end
rescue Exception => e
log "error: #{e}"
end
end
my lib/tasks/resque.rake file is
require "resque/tasks"
task "resque:setup" => :environment
I am able to queue the jobs buts when i try to execute the job using
rake resque:work QUEUE=*
it is throwing an error by saying
argument error
wrong number of arguments (0 for 1)
what am I doing wrong in this?
pjumble is exactly right, you're not passing the page.
Resque.enqueue(Hello, page_id)
enqueue takes the Job followed by the args which go into the perform action. If you had:
class Hello
def self.perform(page_number, page_foo, page_bar)
...
end
end
Then you would do this:
Resque.enqueue(Hello, page_number, page_foo, page_bar)
I am processing my background jobs using Resque.
My model looks like this
class SomeClass
...
repo = Repo.find(params[:repo_id])
Resque.enqueue(ReopCleaner, repo.id)
...
end
class RepoCleaner
#queue = :repo_cleaner
def self.perform(repo_id)
puts "this must get printed in console"
repo = Repo.find(repo_id)
# some more action here
end
end
Now to test in synchronously i have added
Resque.inline = Rails.env.test?
in my config/initializers/resque.rb file
This was supposed to call #perform method inline without queuing it into Redis and without any Resque callbacks as Rails.env.test? returns true in test environment.
But
"this must get printed in console"
is never printed while testing. and my tests are also failing.
Is there any configurations that i have missed.
Currently i am using
resque (1.17.1)
resque_spec (0.7.0)
resque_unit (0.4.0)
I personally test my workers different. I use RSpec and for example in my user model I test something like this:
it "enqueue FooWorker#create_user" do
mock(Resque).enqueue(FooWorker, :create_user, user.id)
user.create_on_foo
end
Then I have a file called spec/workers/foo_worker_spec.rb with following content:
require 'spec_helper'
describe FooWorker do
describe "#perform" do
it "redirects to passed action" do
...
FooWorker.perform
...
end
end
end
Then your model/controller tests run faster and you don't have the dependency between model/controller and your worker in your tests. You also don't have to mock so much things in specs which don't have to do with the worker.
But if you wan't to do it like you mentioned, it worked for me some times ago. I put Resque.inline = true into my test environment config.
It looks like the question about logging never got answered. I ran into something similar to this and it was from not setting up the Resque logger. You can do something as simple as:
Resque.logger = Rails.logger
Or you can setup a separate log file by adding this to your /lib/tasks/resque.rake. When you run your worker it will write to /log/resque.log
Resque.before_fork = Proc.new {
ActiveRecord::Base.establish_connection
# Open the new separate log file
logfile = File.open(File.join(Rails.root, 'log', 'resque.log'), 'a')
# Activate file synchronization
logfile.sync = true
# Create a new buffered logger
Resque.logger = ActiveSupport::Logger.new(logfile)
Resque.logger.level = Logger::INFO
Resque.logger.info "Resque Logger Initialized!"
}
Mocking like daniel-spangenberg mentioned above ought to write to STDOUT unless your methods are in the "private" section of your class. That's tripped me up a couple times when writing rspec tests. ActionMailer requires it's own log setup too. I guess I've been expecting more convention than configuration. :)
I'd like to run a rake task (apn:notifications:deliver from the apn_on_rails gem) from a delayed_job. In other words, I'd like enqueue a delayed job which will call the apn:notifications:deliver rake task.
I found this code http://pastie.org/157390 from http://geminstallthat.wordpress.com/2008/02/25/run-rake-tasks-with-delayedjob-dj/.
I added this code as DelayedRake.rb to my lib directory:
require 'rake'
require 'fileutils'
class DelayedRake
def initialize(task, options = {})
#task = task
#options = options
end
##
# Called by Delayed::Job.
def perform
FileUtils.cd RAILS_ROOT
#rake = Rake::Application.new
Rake.application = #rake
### Load all the Rake Tasks.
Dir[ "./lib/tasks/**/*.rake" ].each { |ext| load ext }
#options.stringify_keys!.each do |key, value|
ENV[key] = value
end
begin
#rake[#task].invoke
rescue => e
RAILS_DEFAULT_LOGGER.error "[ERROR]: task \"#{#task}\" failed. #{e}"
end
end
end
Everything runs fine until the delayed_job runs and it complains:
[ERROR]: task "apn:notifications:deliver" failed. Don't know how to build task 'apn:notifications:deliver'
How do I let it know about apn_on_rails? I'd tried require 'apn_on_rails_tasks' at the top of DelayedRake which didn't do anything. I also tried changing the directory of rake tasks to ./lib/tasks/*.rake
I'm somewhat new to Ruby/Rails. This is running on 2.3.5 on heroku.
Why don't do just a system call ?
system "rake apn:notifications:deliver"
I believe it's easier if you call it as a separate process. See 5 ways to run commands from Ruby.
def perform
`rake -f #{Rails.root.join("Rakefile")} #{#task}`
end
If you want to capture any errors, you should capture STDERR as shown in the article.