Filtering examples in RSpec

It is common for web applications to interface with external services. When testing, since depending on an external service is very fragile, we end up mocking the interaction with such services. However, once in a while, it is still a good idea to check if the contract between your application and the service is still valid.

For example, this week we had to interact with a SOAP service, let’s call it KittenInfo (why would someone provide kitten information via a SOAP service is beyond the scope of this blog post). We only need to contact one end-point of the KittenInfo and it is called get_details, which receives a kitten identifier and returns kitten information:

KittenInfo::Client.new.get_details("gorbypuff")

Since this API is simple, it is very easy to mock the client whenever it is required by our application. On the other hand, we still need to verify that the integration between KittenInfo SOAP service and our application works correctly, so we write some tests for it:

describe KittenInfo::Client do
  it "retrieves kitten details" do
    client  = KittenInfo::Client.new
    details = client.get_details("gorbypuff")
    details[:owner].should == "tenderlove"
  end
end

However, since this is actually contacting the SOAP Service, it may make your test suite more fragile and slower, even more in this case, in which the SOAP Service responses take as long as kitten’s staring contests.

One possible solution to this problem is to make use of filter tags to exclude the SOAP integration tests from running, except when explicitly desired. We could do this by simply setting:

describe KittenInfo::Client, external: true do
  # ...
end

Then, in your spec_helper.rb, just set:

RSpec.configure do |config|
  config.filter_run_excluding external: true
end

Now, running your specs will by default skip all groups that have :external set to true. Whenever you tweak the client, or in your builds, you can run those specific tests with:

$ rspec --tag external

Notice that filter mechanism is similar to how we enable JavaScript tests when using Capybara. This means that, when using Capybara, you could also run all JavaScript tests in your app via $ rspec --tag js or all non-JavaScript tests with $ rspec --tag ~js.

What about you? What is your favorite RSpec trick?

4 responses to “Filtering examples in RSpec”

  1. Cainã Costa says:

    You can add this flag to automatically convert symbols to hash with true values, so you can create an example like this:

    RSpec.configure do |c|
    c.treat_symbols_as_metadata_keys_with_true_values = true
    end

    it “works”, :focus do
    should be_nice
    end

    You can also add a name to your subject:

    subject(:serializer) { Serializer.new(foo) }

    it “serializes” do
    expect(serializer).to be_serializable # this works
    should be_serializable # this too!
    end

    And I really like this “should something” syntax.

  2. John Beynon says:

    Why would you not use something like VCR to store the SOAP response so you don’t have to hit the external service in your tests?

  3. josevalim says:

    John Beynon, the goal of the tests above is to actually hit the server since once in a while to guarantee the contract between the application and the external service is still valid.

    That said VCR is still a good option since it allows you to hit the server by simply cleaning up the cassettes cache and SOAP can be used over HTTP. But depending on the type of external service, you don’t have automatic tools like VCR, so the tip above is still handy.

  4. josevalim says:

    Thanks for sharing!