Restspec Gem Version Build Status

Restspec is a REST api framework built in top of RSpec to help you write robust and mantainable tests to ensure that your api behaves exactly as you want.

Installation

Install it globally like this:

$ gem install restspec

Usage

For a basic tutorial of how to use Restspec, please check this file.

The Restspec Approach

You can skip this section but i think it will help you understand how things happens here. The Restspec design is founded in the separation of how your api components are modeled and what the actual tests are. In Restspec, you have three files intended to model your api:

  • endpoints.rb
  • schemas.rb
  • requirements.rb

The only one that is completely necesary is the endpoints.rb file. This file is when you define what your endpoints are and give them names. For example, the following endpoint: GET /users/:id/orders can be mapped to an endpoint named users/orders. This name can be used to reference and execute the endpoint in the tests instead of repeating it many times.

The next one, schemas.rb, represents the attributes your entities are made of, and the final one, requirements.rb only helps to ensure that some information is already present on the system when the tests begin.

When we talk about endpoints as objects to reuse, we can think of the dependencies between the endpoints as dependencies between models. We can just make the product/show endpoint to depend on the products/index and the products/create endpoints to make sure that the test always run with a product.

Setup

To create a new test for a given api, just run the following command:

$ restspec my-api-tests --api-prefix=http://my-api-domain/api/v1

This will create a folder called my-api-tests with the following contents:

.
├── Gemfile
├── Gemfile.lock
└── spec
    ├── api
    │   └── restspec
    │       ├── endpoints.rb
    │       ├── requirements.rb
    │       ├── schemas.rb
    │       └── restspec_config.rb
    ├── spec_helper.rb
    └── support
        ├── custom_macros.rb
        └── custom_matchers.rb

Your tests go anywhere (you can put them in the api/ folder), the configuration file (restspec_config.rb) has his own documentation inside and the endpoints and schemas file are were you define the inner parts of the expected API. requirements.rb is a set of requirement to assert needs before some tests, like to test that an api key is valid or that the system already has some important data that can't be created through the api.

Endpoints

In the endpoints file you can define your endpoints using an special dsl, like this:

resources :products do
  collection do
    get :index
  end

  member do
    get :show
  end
end

Check the endpoint DSL documentation here for more details of the available methods and options. The only important thing about them is that they are the endpoints we have to test.

Schemas

Schemas are how your data is shaped and they enforces the shape of the your data and how to generates random examples for that. You can define schemas using a special dsl like this:

schema :product do
  attribute :name, string
  attribute :code, string
  attribute :price, decimal | decimal_string
  attribute :category_id, schema_id(:category), :for => [:payload]
  attribute :category, embedded_schema(:category), :for => [:response]
end

schema :category do
  attribute :name, string
end

As you can see, a schema is compound of attributes that are attached to one type. The types are useful for many things. The types documentation is located here.

The schemas DSL documentation is located here. Schemas are a very important part of Restspec but they are not as necesary as the endpoints.

Tests

To actually test something, let's create any test of type api (because in the restspec/restspec_config file we added helpers and macros only for this type of tests). To test an endpoint of your api you have to use the endpoint macro

endpoint 'products/index' do
end

To actually test an execution of the endpoint, you have to use the test method. Restspec tests are not unit ones because the cost of call a possible slow api can be high. Because of this, all the its blocks that are under a test block will only execute the endpoint once. Because of this, we can test many assertions against only one api execution:

endpoint 'products/index' do
  test do
    it { should have_status(:ok) }
    it 'returns an array' do
      expect(body).to be_kind_of(Array)
    end
  end
end

We can call other endpoints from inside a test. For example:

endpoint 'products/create' do
  # ...
  test 'Without a price' do
    # This is the `payload` macro, to fill the request's payload
    payload name: 'random name'

    it 'has the price set to 0' do
      product = read_endpoint(url_params: { id: body.id })
      expect(product.price).to eq(0)
    end
  end
end

Aditionally, we can test an endpoint that is intended to represent an action over a resource. For example, an update can be tested like this:

endpoint 'products/update', resource: 'products/show' do
  # initial_resource is the initial representation of the resource
  payload do
    { name: "#{initial_resource.name}-001" }
  end

  test do
    it 'updates the name' do
      # final resource is the representation of the resource after
      # the endpoint has been executed
      expect(final_resource.name).to_not eq(initial_resource.name)
      expect(final_resource.name).to eq(request.payload.name)
    end
  end
end

For more information about what can you do in your tests, you can see what are the available matchers, the available helpers and the available macros.

Requirements

They allow to check for something important before starting to test.

requirement :check_warehouse_availability do
  execution do
    warehouses = read_endpoint('warehouses/index')
    if !warehouses || warehouses.empty?
      add_error "There is no warehouses in the system"
    end
  end
end

To use them, you have to do the following in your test:

endpoint 'products/create' do
  ensure! :check_warehouse_availability
end

If you have some issue

Be sure to add your issue with a bug label or the question one.

A note about SemVer

We are using SemVer from 0.0.4 onwards. Versions before 0.0.4 doesn't use Semver and they add some features (intended for MINOR releases, not for PATCH releases). With the addition of 0.1, we will be using a CHANGELOG.md file with the track of the commits that made their way to that version.

Contribute

Please be sure to have the EditorConfig plugin in your text editor to follow the guidelines proposed in the .editorconfig file. To contribute, please send us a PR and make sure that the current tests keeps working after that. You can help to complete the unit tests too.