Testing Rails exception handling
I recently found a good article by Neeraj on rescue_action_in_public that saved a good amount of time for me while testing/modifying the rescue behaviour in ApplicationController.
But, testing it was not so trivial. Typicaly, the test.rb will be configuring all test requests to be considered local by through config.action_controller.consider_all_requests_local = true
Now, we need to override this setting only for a few test cases. Also, local_request? must be made to return false.
The former is quite straight forward, done by adding the following line of code to your test case.
@controller.consider_all_requests_local = false
For local_request? to return false, we must set @request.remote_addr to something other than 0.0.0.0 for the test framework to consider the request to be not local.
Rails provides a cleaner way to do this. Calling rescue_action_in_public! will do the job for us. So, putting the pieces together, here is a sample test code to get started
class DummyController < ApplicationController
# Add actions for triggering different error cases (404 - Not Found, 403 - Forbidden, etc.,). Or have a single action and stub it.
# Some dummy action throwing an exception
def some_action
raise "Some error"
end
end
class ErrorHandlingTest < ActionController::TestCase
tests DummyController
def setup
super
@controller.consider_all_requests_local = false
rescue_action_in_public!
end
def test_rescue_exception
get :some_action
# Assert the expected behaviour as follows
# assert_template "#{RAILS_ROOT}/app/views/common/500.html"
end
end
Performance problems with AssociationCollection
While debugging down some performance problem recently, i narrowed down the problem to a piece of code that iterated a has_many association using each method. To check whether the block passed to ‘each’ is the culprit, I emptied the block; that din’t help much. Finally i changed the call to AssociationCollecton.to_a#each, and groovy!, it reduced the time taken by that whole block by more than 10 times. Since that piece of code was called thousands of time in the request i was trying to optimize, it was an optimiziation in order of seconds for that request. What more would i ask?!
I took that chance to learn the AssociationProxy and AssocationCollection code to find what exactly was causing the delay. I learn’t that AssociationCollection delegats all unknown calls to it to @target, the actual collection Array, through method_missing. So, each call was also going through the method_missing path, creating so much delay. Following is a piece of code explains the above behaviour.
t = Time.now
# Replace with your real object and association
some_object.some_has_many_assoc.each { #empty}
puts &quot;#{(Time.now - t) * 1000000} microseconds&quot;
To validate my understanding, i tried some_object.some_has_many_assoc.send(:load_target).each{} , so as to call the ‘each’ method directly on the collection; that ran many times faster than the previous version.
I knew method_missing can lead to performance problems, but din’t expect it to happen at such a magnitude!
1 comment