emulating server errors

Following on from yesterday’s article.

The point of writing our own XML API server was to test the API handling parts of our real system, and part of that testing needed to cover possible error conditions to make sure we recover from them correctly. So it would be useful if we could easily choose an error that would occur for a given test. I decided the Rails filter mechanism would be a good place to start.I wanted to be able to add new error conditions easily, so I wrote each one in a separate class/file in the controller directory, each implementing the Around Filter interface1. A simple controller called ErrorController lets the user choose which filter they want. The interesting method of this controller is:

private
def load_filter_list
Dir["#{File.dirname(__FILE__)}/*_filter.rb"].select{|fn| File.file? fn}.map{|fn| File.basename fn, ".rb"}
end

This just finds all the files in the controller directory that end in “filter.rb”. The rest of the controller presents them as a list, and saves the user’s choice.

The more difficult task was to make my RequestController actually use the current filter. To avoid the controller having to deal directly with changing filters, I wrote a wrapper class that could delegate to the chosen filter:

class Simulator
def initialize
@ocache = {}
end
 
def delegate
current_filter = (s = Setting.find(:first)) && s.error_filter
@ocache[current_filter] ||= instance_eval("#{current_filter.classify}.new") if current_filter
end
 
def before(controller)
delegate.before(controller) if delegate
end
 
def after(controller)
delegate.after(controller) if delegate
end
end

This class is then attached to the RequestController as an around filter. On each request it will check what the current selected filter is, create an instance of it if it doesn’t already have one, and then delegate the before and after methods to it.

Here’s an example filter class, which simulates the XML output of the server being changed unexpectedly2:

class XmlChangeFilter
def before(controller)
end
 
def after(controller)
#grab the response body and convert it back to rexml
xml = REXML::Document.new(controller.response.body)
 
#insert our fake element
fake = REXML::Element.new "fake"
fake.text = "Shouldn't be here"
xml.root.elements["//response"] << fake
 
#write back to the response
controller.response.body = ""
xml.write(controller.response.body)
end
end

Using a variety of these filters to simulate error conditions enabled us to test out and significantly improve our server’s error handling, so it was very helpful.


fn1. Ok, Ruby classes don’t strictly implement interfaces in the way that Java does, but the idea is the same.

2 Whether it’s a bad thing that our actual server is sensitive to small changes in XML output is a separate discussion!

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*