Testing Rails jBuilder JSON APIs With RSpec
When I first embarked on creating an API for a project, I couldn’t find a ton of resources on how to test it with RSpec, so I decided to write a blog post about it.
Let’s say we’re building an app that allows users to list their second bedrooms to short-term guests. We all love Airbnb :)
This would be an application with many models that have complex associations, but let’s focus on what a host can do with their listing(s). They can create a new one, edit it, and delete it. A user can view one listing, or many listings (given certain filters like price, listing type, availability, city, etc). Let’s focus on the GET /listings
, POST /listings
, and DELETE /listings/:id
requests that our API would support.
Here’s the listings controller. This controller ultimately inherits from ActionController::Base
, and I’m using jbuilder to format the rendered JSON. Pretty straightforward.
The Tests
The first thing we need to do is tell each test to render_views
, as we’re rendering JSON via jbuilder, and these are handled by Rails as views. This should go at the top before any tests.
1 2 3 4 5 6 |
|
GET
The GET /listings request should return all of the listings, with or without a filter. A simple test for this needs to:
- make a GET request to the index method and specify the format we’re responding with (json)
- parse the response body into a JSON object so we can see what we’re getting back from the request (which I’ve done once, above, in a
let
block, because this is a repeated action) - body looks like this:
1 2 3 4 5 6 |
|
- grab some of that JSON data to assert an expectation on
- test that the data is what we expect it to be (against some data in our test database, in this case I just built this in a
before(:each)
block in myrails_helper
)
Here we go:
1 2 3 4 5 6 7 8 9 10 |
|
Next let’s test that we can return all the listings of a certain price, price being a parameter. In my listings controller, there’s an index
method with a conditional to check for a params[:price]
, and if that was given, return all listings where
“price” is that price. Our test for this will look very much like the above test, except we’re accounting for price, which is passed into a get :index
method along with format.
1 2 3 4 5 6 7 8 9 |
|
POST
Now let’s work on testing our POST /listings.json
request. We’re going to test that it can work (creating a new listing object in our database) and that it can fail (returning a 422).
Our success test will function much like our test for our above get requests. Instead it will call the post :create
method which specifies the format as well as the required params for :listing
, which we specified in a private method listing_params
in our controller.
1 2 3 4 5 6 7 8 9 10 |
|
Let’s also test that a failure will happen without the expected params (setting neighborhood_id
to nil). There’s a validation on the listing model where a listing must have an associated neighborhood, which I’ve already tested in a model test. However, I wanted to test that the controller would return a 422 if the validation doesn’t pass. Here we go:
1 2 3 4 5 6 7 8 9 |
|
DELETE
Last, let’s test deleting a listing. We want to call the delete :destroy
method with the format and the id params specified (we’re going to delete the first item in our test database). We want to test that the controller destroy
method deletes the record from the database.
1 2 3 4 5 6 7 8 9 10 11 |
|
There we go! RSpec makes it easy to test Rails APIs by simulating HTTP requests via get, post, patch, and delete methods. Below are some gists of the code in full discussed in this post.