A Test Harness in Ruby
To test some of these ideas, my team implemented a simple record-and-playback style test harness in Ruby (www2.ruby-lang.org/en/). We decided that Ruby's dynamic programming and metaprogramming features would be useful in creating a harness that could add methods declaratively at runtime. Since our team consisted of Java developers trying Ruby for the first time, we decided to use JRuby (jruby.codehaus.org). This let us incorporate well-understood Java frameworks from within the Ruby source.
Using SOAP4R (dev.ctor.org/soap4r), we wrote a SOAP Client. Data was then recorded from an existing Apache Axis2 service. The output was saved using YAML (www.yaml.org), a powerful human-readable data serialization mechanism available with Ruby implementations. We also used SOAP4R to create a standalone SOAP Server. Methods to be tested and the corresponding results were maintained as key-value pairs in a Java properties file. The format of each key-value pair is:
methodname_param1param2... paramN=recorded_response.yml
If desired, we could have extended the format to refer to specific service URIs as well. Finally, we added methods at runtime using Ruby's metaprogramming facilities. Example 1 created the methods. The Java classes (Properties, FileInputStream) are leveraged by way of JRuby. Example 2 presents the corresponding client code used to record data from the web service.
#read the methods and responses from test.properties fis = FileInputStream.new("test.properties") @properties = Properties.new @properties.load(fis) fis.close keys = @properties.keys #add methods to return the recorded response keys.each do |key| puts "Adding method " + key.to_s add_method(self,key.to_s, Object.new) self.class.send('define_method', key.to_s) { |*args| if (@properties.containsKey(key.to_s)) value= @properties.get(key) puts "Found operation " + key.to_s + " with value " + value.to_s #retrieve the data from the saved result if (value) if (value.rindex('.yml') > 0) data = YAML::load_file(value) return data else file = File.new(value) data = file.readlines file.close return data end end end } end
While this example is simple, it provides the skeleton of a test harness for services. With moderate effort, you can extend it to encompass multiple services with multiple methods. Consequently, the server can be used to create a boundary of services to be invoked by the service(s) under study.
driver = SOAP::RPC::Driver.new( 'http://localhost:8080/axis2/services/WeatherService', 'http://ws.apache.org/axis2') #driver = SOAP::RPC::Driver.new( 'http://localhost:9080/','urn:weatherSoapServer') driver.add_method('getWeather') file = File.new('getweather.yml','w') YAML.dump(driver.getWeather,file) file.close
To the best of my knowledge, there are two commercial off-the-shelf solutions that provide this sort of functionality existiTKO's LISA (www.itko.com) and AmberPoint's Validation (www.amberpoint.com/products/validation.shtml). Both provide Service Virtualization to varying degrees. While Amberpoint's solution is a development time record-and-playback utility, iTKO's solution also has elements for continuous testing of services.