Rester allows you to test your APIs using a non-programmatic or non-GUI based approach, which are some of the more conventional ways of testing RESTFul APIs. Rester is inspired by various unit testing frameworks like JUnit, 'unittest' (python) etc and is conceptually organized like those frameworks, but is geared towards testing RESTful API endpoints. With Rester, all tests are specified in YAML or JSON.
Testing RESTful APIs generally involves two prediticable steps -
Most testing tools available for testing RESTful APIs use some sort of a GUI based approach which doesn't lend itself towards re-use, better code organization, abstraction and some of the other benefits that are generally available with more programmatic frameworks like JUnit. Programmatically building test cases provides the highest level of flexibility and sophistication, but the downside to this approach is that it ends up with lots of fairly tedious and repetitive code. Conceptually, Rester is similar to existing unit testing frameworks, but it uses JSON or YAML (instead of a programming language) to implement/specify the actual tests. Programmers and non-programmers can reap the benefits with the Rester approach.
Note: As of now Rester only supports APIs that don't require explicit authentication of calls, but future versions will support OAuth. Rester was mainly created to test internal APIs that generally bypass the need for authentication of the calls.
Globals can be defined in the either a TestSuite or TestCase.
globals:
variables:
request_opts: # special for the request library.
verify: false # disable CERT checking for SSL
http: "http://localhost:8905"
https: "https://localhost:8081"
test_cases:
- servers.yaml
- cors.yaml
- xdomain.yaml
- notfound.yaml
name: "Ping"
globals:
variables:
http: "http://localhost:8905"
https: "https://localhost:8081"
testSteps:
- ...
- ...
A TestStep contains the following:
URL is the only mandatory param.
Example of a TestStep (JSON):
{
"testSteps": [
{
"name":"Name of TestStep",
"apiUrl":"http://example/api/v1/helloworld/print",
"asserts":{
"headers":{
"content-type":"application/json; charset=utf-8"
},
"payload":{
"message":"Hello World!"
}
}
}
]
}
A complete example as YAML, leveraging the yaml references:
name: "Ping"
globals:
variables:
http: "http://localhost:8905"
https: "https://localhost:8081"
testSteps:
- &test1
name: "ping http"
apiUrl: "{http}/ping"
method: "get"
raw: true
asserts:
headers:
connection: "keep-alive"
content-type: "text/plain; charset=utf-8"
payload:
__raw__: "pong"
-
<<: *test1
name: "ping https"
apiUrl: "{https}/ping"
See: https://gist.github.com/ninowalker/1fe8aad019feab3fe265
pip install git+https://github.com/chitamoor/Rester.git@master
Run the default test case -
apirunner
This will look for the default test case, test_case.json in the current directory
Run a specific test case
use the command line option --tc=
e.g. invoke a test case specified in the file "./rester/examples/test_case.json"
apirunner --tc=./rester/examples/test_case.json
Run a specific test suite
use the command line option --ts=
e.g. invoke a test suite specified in the file "./rester/examples/test_suite.json"
apirunner --ts=./rester/examples/test_suite.json
Adjust the log output or details
Rester support varying levels of logs - DEBUG, INFO, WARN, ERROR. You can
specify the level using the command line option --log=
e.g. run the API with INFO level
apirunner --log=INFO
Just dump the JSON output
Specify the HTTP headers as part of an API request
testSteps: [
{
"name":"Name of TestStep",
"apiUrl":"http://example/api/v1/helloworld/print",
"headers":{
"content-type":"application/json;"
},
....
}
]
Specify the URL params as part of an API request. There are two ways to specific URL params, which are mentioned below -
testSteps: [
{
"name":"Name of TestStep",
"apiUrl":"http://example/api/v1/helloworld/print",
"headers":{
...
},
"params":{
"param_1":"value1",
"param_2":"value2"
},
....
}
]
testSteps: [
{
"name":"Name of TestStep",
"apiUrl":"http://example/api/v1/helloworld/print?param_1=value1¶m_2=value2",
....
}
]
Perform an HTTP POST
testSteps: [
{
"name":"Name of TestStep",
"apiUrl":"http://example/api/v1/helloworld/print",
"headers":{
...
},
"method":"post"
"params":{
"param_1":"value1",
"param_2":"value2"
},
....
}
]
Get a non-JSON response, using the raw option.
name: "Ping"
globals:
variables:
http: "http://localhost:8905"
testSteps:
- name: "ping http"
apiUrl: "{http}/ping"
method: "get"
raw: true
asserts:
headers:
content-type: "text/plain; charset=utf-8"
payload:
__raw__: "pong"
As mentioned previously, all of the assert statements are specified within an asserts element
Assert "content-type" HTTP header
testSteps: [
{
"name":"Name of TestStep",
"apiUrl":"http://example/api/v1/helloworld/print?param_1=value1¶m_2=value2",
....
}
"asserts":{
"headers":{
"content-type":"application/json; charset=utf-8"
},
....
}
]
"output.body" contains the word 'launched'
testSteps: [
{
"name":"Name of TestStep",
"apiUrl":"http://example/api/v1/helloworld/print?param_1=value1¶m_2=value2",
....
}
"asserts":{
"headers": {
....
},
"payload":{
"output.level":2,
"output.result":"Message Success",
"output.status":"-gt 3",
"output.body":"exec 'launched' in value"
},
....
}
]
-gt - greater than
e.g. parent.child > 3
"payload":{
"parent.child":"-gt 3",
}
-ge - greater than eqal to
e.g. parent.child >= 3
"payload":{
"parent.child":"-ge 3",
}
-lt - lesser than
e.g. parent.child < 2
"payload":{
"parent.child":"-lt 2",
}
-le - lesser than eqal to
e.g. parent.child <= 2
"payload":{
"parent.child":"-le 2",
}
-ne - not eqal to
e.g. parent.child.message != "success"
"payload":{
"parent.child.message":"-ne success",
}
-eq - equal to
e.g. parent.child.message == "success"
"payload":{
"parent.child.message":"-eq success", # either will work
"parent.child.message":"success",
}
e.g. parent.child.message == "hello world"
"payload":{
"parent.child.message":"exec len(value) > 7 and value.endswith('world')",
}
Values from the payload can be extracted and assigned to variables in the variable name space. The assignments are specified as part of the postAsserts element and should be placed right after the asserts element.
"asserts":{
"headers": {
....
},
"payload":{
"output.result":"Message Success",
....
},
....
},
"postAsserts": {
"variable_for_next_step":""output.id"
....
}
check if parent.child.message is a String
"payload":{
"parent.child.message":"String",
}
check if parent.child.version is an Integer
"payload":{
"parent.child.version":"Integer",
}
check if parent.child is an Object
"payload":{
"parent.child":"Object",
}
Sample payload
"entries":[{
"id":1
},
{
"id":2
},{
"id":3
}
]
}
For the above payload, verify that entries is an Array element
"payload":{
"entries":"Array"
}
Verify the length of the entries Array element
"payload":{
"entries._length":3,
}
Verify the first and the second element of the entries Array element
"payload":{
"entries[0].id":1,
"entries[1].id":2
}
Variables are declared in the "globals" section of the TestSuite
"globals":{
"variables":{
"baseApiUrl":"https://example.com",
"api_key":"YOUR_KEY",
"rule_key":"CONFIG_KEY"
}
},
...
testSteps: [
{
"name":"Name of TestStep",
"apiUrl":"http://{baseApiUrl}/api/v1/helloworld/print?param_1=value1
....
}
]
Run the docker image raseev/rester
docker run -it --rm --name rester raseev/rester apirunner --ts examples/weather/test_suite.json
docker run -it --rm --name rester raseev/rester apirunner --ts examples/imdb/test_suite.json
Use the docker volume option to specific the directory that contains the tests
docker run -v $(pwd)/rester/examples:/tests -it --rm --name rester raseev/rester apirunner --ts tests/imdb/test_suite.json --log=DEBUG
rajeev@chitamoor.com
Nino Walker - nino@livefyre.com
Unreleased
__raw__
to raw
on the TestStep.status
to TestStep.asserts, allowing for non-200
replies.nose
, to leverage all the things.
eval
for all tests, because expressiveness; value == '123'
instead of 123
.eval
tests.time: time.time()
record mode
to capture responses for testing directly.