restest
restest solves 2 problems in REST API testing:
- It makes you more productive.
- Tests are more maintainable.
Installation
Installation is simple.
npm install restest -g
First test
To run the tests mentioned in this README, please do the following:
npm install restest --save-dev
Now, cd to node_modules/restest directory and issue the command:
node examples/restest-test-server.js
This will bring a test server that supports create/read/update/delete operations for a user resource.
Now, create a file tests.yaml
and start describing your tests:
# This is the id of the test.create_user: # You can tag your tests. This is super useful during execution when you # want to run tests with specific tags. tags: [crud, user] # Each test is a series of API request and expected responses. We call them # steps. This test has only one step. steps: # Each test step has 5 parameters: uri (required), http method (deafult # GET), request payload (optional), expected status (default 200) and # expected response ( optional) - uri: "http://localhost:3030/restest-examples/users" method: "POST" payload: name: "Ram Sharma" email: "ram@sharma.com" expected_status: 200 expected_response: name: "Ram Sharma" email: "ram@sharma.com"
Now, to run the test
restest tests.yaml
Using variables in tests
It's bad to hardcode your hostname and port in the test. Solution to that is restest variables. You can define some variables while invoking restest and use them throughout your tests. So, you could re-write the uri in your test as:
- uri: "{{uri_prefix}}/users"
and invoke restest as:
restest -d uri_prefix="http://localhost:3030/restest-examples" tests.yaml
You can use your variables anywhere you use a string in uri, method, payload & expected_response.
Extended expected response
Your response can have deeply nested objects and you can still expect them. Example:
create_user: tags: [crud, user] steps: - uri: "http://localhost:3030/restest-examples/users" method: "POST" payload: name: "Ram Sharma" email: "ram@sharma.com" phones: - type: home number: 123456789 - type: office number: 987654321 address: city: Bangalore country: India expected_response: name: "Ram Sharma" email: "ram@sharma.com" phones: - type: office number: 987654321 - type: home number: 123456789 address: city: Bangalore country: India
Note about the phone number, restest doesn't check for order while matching arrays.
Multi-Request tests
Now, let's run a test that would create a user and verify the creation by doing a GET.
create_and_get_user: tags: [crud, user] steps: - uri: "{{uri_prefix}}/users" method: "POST" payload: name: "Ram Sharma" email: "ram@sharma.com" expected_response: name: "Ram Sharma" email: "ram@sharma.com" - uri: "{{uri_prefix}}/users/{{responses.[0].id}}" expected_response: name: "Ram Sharma" email: "ram@sharma.com"
Did you notice that we've added another step in our test now? The second step does a GET (default method) by user id and expects the same name & email as we gave in previous step.
We'll talk about the {{responses.[0].id}}
in next section.
More on variables in tests
Each string value in the test (be it uri, method, payload values or expected response values) are evaluated using Handlebars templating engine. And during template evaluation, following variables are set:
- All variables specified using
-d
command line flag. payloads
andresponses
are special variables. These are arrays of payloads sent & responses received during this test. In this case, when the first step completes, it's payload object is pushed intopayloads
array and received response object is pushed intoresponses
array.this_payload
is another special variable. It's available in theexpected_response
section and holds the entire payload sent as part of current request.
So, the following line:
- uri: "{{uri_prefix}}/users/{{responses.[0].id}}"
{{responses.[0].id}}`` points to the value of
id` in the response received in step 1.
We could re-write the entire test as follows:
create_and_get_user: tags: [crud, user] steps: - uri: "{{uri_prefix}}/users" method: "POST" payload: name: "Ram Sharma" email: "ram@sharma.com" expected_response: name: "{{this_payload.name}}" email: "{{this_payload.email}}" - uri: "{{uri_prefix}}/users/{{responses.[0].id}}" expected_response: name: "{{payloads.[0].name}}" email: "{{payloads.[0].email}}"
Templates
Given that we'll need to create a user for multiple tests, it would be good to templatize it and use the template in tests.
Let's rewrite the previous example with templates:
# A template for creating usertmpl_create_user: uri: "{{uri_prefix}}/users" method: "POST" payload: name: "Ram Sharma" email: "ram@sharma.com" expected_status: 200 expected_response: name: "{{this_payload.name}}" email: "{{this_payload.email}}" create_and_get_user: tags: [crud, user] steps: - template: tmpl_create_user - uri: "{{uri_prefix}}/users" method: "POST" payload: name: "Ram Sharma" email: "ram@sharma.com" expected_response: name: "{{this_payload.name}}" email: "{{this_payload.email}}" - uri: "{{uri_prefix}}/users/{{responses.[0].id}}" expected_response: name: "{{payloads.[0].name}}" email: "{{payloads.[0].email}}"
Let's see what we did around here. First we created a template for API that can be used for creating user:
# A template for creating usertmpl_create_user: uri: "{{uri_prefix}}/users" method: "POST" payload: name: "Ram Sharma" email: "ram@sharma.com" expected_status: 200 expected_response: name: "{{this_payload.name}}" email: "{{this_payload.email}}"
A template has five attributes: uri, method, payload, expected_status and expected_response. Same as any test step.
To use a template in a test step, just use the keyword template
:
- template: tmpl_create_user
Overriding template content
Let's revisit the create_user test with the use of tmpl_create_user
template that we just created:
create_user: tags: [crud, user] steps: - template: tmpl_create_user
Now, what if we wanted to provide a different email id instead of the one provided in template? And what if we want to also test the address functionality? Here is how you can do it:
create_user: tags: [crud, user] steps: - template: tmpl_create_user payload: email: "another@email.com" address: city: Bangalore country: India expected_response: address: city: Bangalore country: India
restest
picks up the request parameters (i.e. uri, method, payload, expected_status & expected_response) from the step (if specified) and merges them into the parameters specified at the template level.
In this example, payload
will be taken from the template, email
field of the template payload will be over-ridden by what's specified in test step. address
field will be added now.
restest
uses underscoreDeepExtend for merging payload & expected_response objects. Please refer to its documentation about extension related details.
Generating API documentation
Download the jekyll template from https://github.com/mangarg/restest-jekyll-template. Unzip it in some directory.
Now run the following command:
restest -r jekyll -o <restest-jekyll-template-dir> <files...>
restest
will execute your tests and populate the jekyll directory with API documentation.