Rails 3, SOAP and Testing, Oh My!

This past week at work I have had the “pleasure” of building out a SOAP endpoint for an internal system. This has caused me to find a wonderful new gem Wash Out (https://github.com/inossidabile/wash_out). With a feature comes with new test and with my first SOAP endpoint comes, how to test SOAP endpoints.

Testing a Wash Out controller wasn’t something that was blaintiantly obvious to me and took some experimenting and discussions with Boris Staal (@_inossidabile). Below is an example of how we settled on testing. This may not be the end all perfect solution but hopefully it will help you get started.

Let’s start with a sample controller, this will give us a base to refer to with our tests.

class API::GasPrices < ApplicationController
  include WashOut::SOAP
  soap_action "GetGasPrices", 
              :args   => {:postal_code => :integer}, 
              :return => :string
  def GetGasPrices
    render :soap => GasPrices.find_all_by_postal_code(params[:postal_code]).to_xml.to_s
  end
end

This controller is a fairly standard example, it has one method GetGasPrices and takes a postal_code as an argument. It returns a string of gas prices.

One of the things that we got caught on was how to actually hit the Wash Out gem and execute the HTTP request. To do that we’ll need to mount our app as a rack app.

We’ll need to make sure that we are using a version of the HTTPI gem that can use a Rack adapter. Right now we need to point our HTTPI gem at the GitHub repo. For the actual testing of making SOAP calls we can use Savon.

gem 'httpi', :git => 'https://github.com/savonrb/httpi.git'
gem 'savon'

Next we’ll need to create a spec file for our tests. For this example let’s use a request spec, even though this is a controller we actually want to make a request with SOAP to make sure our methods are recieving information correctly.

|~spec/
| |+requests/
| | |+API/
| | | |-gas_prices_controller_spec.rb

Let’s setup our spec file now, we’ll need to require Savon and the spec_helper.
Then create a describe block like below.

require 'spec_helper'
require 'savon'

describe API::GasPrices do
  HTTPI.adapter = :rack
  HTTPI::Adapter::Rack.mount 'application', MyGasPriceChecker::Application

  it 'can get gas prices for a postal code' do
    application_base = "http://application"
    client = Savon::Client.new({:wsdl => application_base + api_gas_prices_path })
      result = client.call(:get_gas_prices, :message =&gt; { :postal_code => 54703 })
    result.body[:get_gas_prices_response][:value].should_not be_nil
  end

Inside of our describe block we are using the HTTPI rack adapter, and then configuring that adapter to mount our MyGasPriceChecker as application. This will give us the ability to use the url http://application. In our test we’ll create a new Savon client, this client will need to access our WSDL so it can find the operations it has access too.

Once we have a client created our code can now actually call the GetGasPrices SOAP method. Our test then verifies that the value of our response is not nil, this is really just a starting point and we can iterate going forward to test actual return values.

Controller Testing

Recently I have gotten to work on a greenfield application, this has led to some discussions about the best way to test things. I personally have been taking time to write tests that allow me to take small steps giving me a better sense of direction. These small steps have allowed me to write a test, make it pass, write the next test, make it pass then refactor.

Continuously refactoring my code keeps it clean and maintainable. I’m not going for cleverness or golfing to the lowest number of lines of code. Instead I’m going for code that is flexible and allows me to continue to add new features with ease.

Below is an example of a controller test, written in two different styles. We’ll walk through a small refactoring scenario and see how we can keep our test simple and testing result rather than the implementation by comparing the two styles.

Disclaimer this example has some assumptions such as I am using FactoryGirl and Rspec.

Here is our starting controller, we are going to focus on the edit action. This is pretty strait forward, we’re going to edit an instance of ‘Foo’ so we’ll return it to the view.

class FooController > ApplicationController 
  def edit
   @foo = Foo.find(params[:id])
  end
end

One way we can test this is to build an object with FactoryGirl and then stub out the find method on Foo to return that object. This will allow us to test our edit action insuring that foo is always the same thing.

describe "GET 'edit'" do
  it 'should receive the STUBBED Foo instance' do
    @foo = FactoryGirl.build(:foo)
    Foo.stub(:find).and_return(@foo)
    get :edit, :id => @foo.id
  
    expect(assigns(:foo)).to eql @foo
  end
end

Another way we could test this trivial example is to just use FactoryGirl to create the object rather than just build it. This takes a little less code, but does require two hits to the database, one for saving the record and one for retrieving.

The benefit of this is we are setting up that if we send in the ‘id’ of ‘@foo’ we’ll get back an identical ‘@foo’ from the database.

Our controller test now is just saying hey we expect to get ‘@foo’ back, we really don’t care how you get it but in order for this edit form to work we need this ‘@foo’ back.

describe "GET 'edit'" do
  it 'should receive the NON stubbed Foo instance' do
    @foo = FactoryGirl.create(:foo)
    get :edit, :id => @foo.id
  
    expect(assigns(:foo)).to eql @foo
  end
end

Now let’s refactor our Foo controller to do something a little different. Now we decided that really the ‘@foo’ should be retrieved by sending the ‘@foo.id’ to a method called ‘by_bar’

class FooController < ApplicationController 
  def edit
     @foo = Foo.by_bar(params[:id])
  end
end

Here is our new class method that replaces the normal Foo.find.

class Foo
  def self.by_bar(id)
    Bar.find_by_foo_id(id).foo
  end
end

Now in our first version of the test we are going to get an error because we are not actually creating and saving the object and it is only stubbing out the find method making it brittle and tied to the implementation.

Let’s refactor our tests, first we’ll start with the test using a stub, we’ll need to modify the stub to use the ‘by_bar’ method.

Next we’ll look at the test not using a stub, this one does not need any work. Again we are testing to make sure that the instance of ‘Foo’ we are expecting is returned. In this case it is, so we don’t need to do anything.

describe "GET 'edit'" do
  it 'should receive the STUBBED Foo instance' do
    @foo = FactoryGirl.build(:foo)
    Foo.stub(:by_bar).and_return(@foo)
    get :edit, :id => @foo.id
  
    expect(assigns(:foo)).to eql @foo
  end
  
  it 'should receive the NON stubbed Foo instance' do
    @foo = FactoryGirl.create(:foo)
    get :edit, :id => @foo.id
  
    expect(assigns(:foo)).to eql @foo
  end
end

While looking at our refactoring we realize that we didn’t really need a separate method for our ‘Bar.find_by_foo’ and we can just move that into our controller like so.

class FooController < ApplicationController 
  def edit
     @foo = Bar.find_by_foo_id(id).foo
  end
end

Now our stubbed test breaks again, let’s see what it will take to fix it. We’ll have to change our stub again. This time the stub needs to be done on the ‘Bar’ class and ‘find_by_foo_id’ method. Again our non-stubbed test continues to work because we are still returning an instance of ‘Foo’

describe "GET 'edit'" do
  it 'should receive the STUBBED Foo instance' do
    @foo = FactoryGirl.build(:foo)
    Bar.stub(:find_by_foo_id).and_return(@foo)
    get :edit, :id => @foo.id
  
    expect(assigns(:foo)).to eql @foo
  end
  
  it 'should receive the NON stubbed Foo instance' do
    @foo = FactoryGirl.create(:foo)
    get :edit, :id => @foo.id
  
    expect(assigns(:foo)).to eql @foo
  end
end

As we have seen here taking small steps and limiting our stubs and mocks will save us time when refactoring. In all of these examples the things that broke the test were just breaking test setup code rather than actually failing the test giving us a false positive.

Ruby Koans

Recently after doing a Code Retreat with Cory Haines I found myself really wanting to work on my Ruby fu.  I was inspired by the idea of practicing my trade everyday in some simple exercises that  allow me to focus on parts of the language to really learn it well.

So I decided to pick up the Ruby Koans which I was introduced to a year or so ago but never thought I needed that.  I always figured I could just keep learning by using Ruby for my projects, but then I realized that I was just using the same few parts of the language to build my apps.

Following the craftsman analogy, I was using the same saw and hammer because they are comfortable.  I didn’t take the time to learn about the pneumatic framing nailer because I already had a way to pound in nails, but boy does it make building things easier.

So if you are new to Ruby or have been using it for awhile I really recommend you find some exercises to keep your skills sharp and explore new parts of the language. I really think the Ruby Koans are perfect for this, I see them being easy to get started and with enough substance that even seasoned developers will pick up new things.

Pair Programming with Tmux

This morning I decided to try out a new pairing setup.  What I did was setup a machine that can be accessed by both developers and installed tmux.

Tmux works like screen where it creates sessions that you can attach multiple users to.  It also allows you have a few open buffers so you can do things like have one buffer open for Vim and another to run your Rails Server.

So we have taken advantage of the ability to attach multiple users to a session and then had each developer attach via ssh from their local machine.  We haven’t configured tmux beyond the basic install but it seems to be working good.

One nice thing is each developer can SSH back into their local box from a separate buffer and have access to files on their local machine if need be.

-CJ

Mad Railers – May 2011

Last night I had a chance to present to the Madison Ruby on Rails Club.  As the meetings are always a good time, I did really enjoy giving my “Testing any Website with Selenium and Cucumber” talk. You can get the Slides Here.

The talk revolves mostly around getting to the point of using Cucumber to drive your browser based behavior tests.  I feel strongly that at this point in time Sauce Labs is also the easiest way to run those tests.

If you want to get started writing tests head over to my git hub page and grab the “Cucumber Test Harness” project.  Then get yourself an account at Sauce Labs, they give you 200 minuets a month of free testing time. Then follow the readme in the Test Harness project and start testing websites.

Thanks again to everyone that came out.  Remember to please rate the club meeting on the Meetup.com page and also I would appreciated feedback on my SpeakerRate page.

-CJ