Categorygithub.com/thomaspoignant/api-scenario
modulepackage
0.3.0
Repository: https://github.com/thomaspoignant/api-scenario.git
Documentation: pkg.go.dev

# README

API-scenario

Scenario API testing from the command line.

Release version Build Status Coverage Status Sonarcloud Status Go version FOSSA Status Docker Image Version (latest semver)

API-scenario is a simple command line tool that allow you to execute easily a scenario to test your APIs.

It is perfect to make end to end tests, you could use in your CI workflow to validate your deployment or just to test locally your development.

api-scenario demo


Why this project?

Our goal is to have a simple command line tool that can run scenario to test your apis directly from the command line. You can create a scenario and start using it during development, but also use it in your CI/CD to validate your deployment.

You can use variables in your scenario, so you are sure to run the same test suite on each environment and be sure of your release.


Installation

All the binaries are available in the release.

Install using Homebrew (mac and linux)

If you are using Homebrew package manager, you can install api-scenario with Homebrew.

brew tap thomaspoignant/homebrew-tap
brew install api-scenario

Install using Scoop (windows)

If you are on Windows and using scoop package manager, you can install api-scenario with Scoop.

scoop bucket add org https://github.com/thomaspoignant/scoop.git
scoop install api-scenario

Install using .deb file (linux)

If you prefer to use directly the .deb file to install in your debian like linux distribution.
Don't forget to set the correct version number.

wget https://github.com/thomaspoignant/api-scenario/releases/download/vX.X.X/api-scenario_X.X.X_Tux_64-bit.deb && dpkg -i api-scenario_*.deb

Install using .rpm file (linux)

If you prefer to use directly the .rpm file to install in your centos/fedora like linux distribution.
Don't forget to set the correct version number.

wget https://github.com/thomaspoignant/api-scenario/releases/download/vX.X.X/api-scenario_X.X.X_Tux_64-bit.rpm && rpm -i api-scenario_*.rpm

Use docker image

To use the last version of our docker image you can pull thomaspoignant/api-scenario:latest.

docker pull thomaspoignant/api-scenario:latest

You can also pull all the version directly but also directly a major version.
Available images are:

  • docker pull thomaspoignant/api-scenario:latest (target the latest version)
  • docker pull thomaspoignant/api-scenario:vX (target the major version)
  • docker pull thomaspoignant/api-scenario:vX.X (target the minor version)
  • docker pull thomaspoignant/api-scenario:vX.X.X (target a specific version)

See docker hub page for usage.


Command line usage

Version

api-scenario version
# 0.1.0

Execute your scenario

To execute your scenario please use the run options and specify your scenario file.

api-scenario run --scenario="./scenario.json"

There are several options you can use with this command:

OptionShort versionRequiredDescription
--scenario-sInput file for the scenario.
--authorization-token-tAuthorization token send in the Authorization headers.
--header-hHeader you want to override (format should be "header_name:value").
You can have multiple values of this options
--variable-hValue for a variable used in your scenario (format should be "variable_name:value").
You can have multiple values of this options
--verbose-sRun your scenario with debug information.
--quiet-sRun your scenario in quiet mode.
--no-colorDo not display color on the output.
--output-file-fOutput file where to save the result (use --output-format to specify if you want JSON or YAML output).
--output-formatFormat of the output file, available values are JSON and YAML (ignored if --output-file is not set, default value is JSON).

Save result into file

To keep history of your scenario execution you can export the results into a file. You just have to add the option --output-file="<your file location>" and it will save the result into a JSON file (If you prefer YAML result add --output-format=YAML).

Example:

api-scenario run --scenario="./scenario.json" --output-file="<your file location>" --output-format=YAML

Creating Your First Test

Creating a test is simple, you just have to write json or yaml file to describe you api calls, and describe assertions.

The basic structure of the file

YAML
name: Simple API Test Example
description: A full description ...
version: '1.0'
steps:
   - ...
JSON
{
 "name": "Simple API Test Example",
 "description": "A full description of your test scenario",
 "version": "1.0",
 "steps": [
   ...
 ]
}

This global fields allow to describe your scenario:

  • name: The name of your scenario
  • description: A complete description of what your scenario is doing
  • version: The version of your scenario
  • steps: Array of steps, it will describe all the steps of your scenario (see steps for more details).

Our first step

For our first step we will create a basic call who verify that an API answer with http code 200 when calling it.

YAML
- step_type: request
  url: {{baseUrl}}/api/users
  method: GET
  headers:
    Content-Type:
      - application/json
  assertions:
    - comparison: equal_number
      value: '200'
      source: response_status
JSON
{
  "step_type": "request",
  "url": "{{baseUrl}}/api/users",
  "method": "GET",
  "headers": {
    "Content-Type": ["application/json"]
  },
  "assertions": [
    {
      "comparison": "equal_number",
      "value": "200",
      "source": "response_status"
    }
  ]
}

We manipulate different concepts here.

  • step_type: The type of the step, here we are using request because we want to test a rest API (see steps to see the list of available step types).
  • url: The URL of our request, {{baseUrl}} will be replaced before the call (see Using Variables in Requests for details)
  • method: The HTTP verb of our request.
  • headers: The list of headers we sent with the request.
  • assertions: This is the list of checks we are doing when we have received the response.
    • comparison: The type of check we are doing, here we are testing that number are equals.
    • value: The expected value
    • source: On what part of the response we are looking, here we are checking the response status.

Now the first scenario is build, we can run it (see complete scenario: YAML / JSON).

api-scenario run --scenario="examples/first-test.json" --variable="baseUrl:https://reqres.in"

What we are doing is here is running our scenario file, and we ask to replace every occurrence of {{baseUrl}} by https://reqres.in.


Steps

There are different types of steps who allow performs different types of actions.
To specify the type of a step we are using the property step_type is the step object.

If there is no step_type property we ignore the step.

Pause

pause is simple, it is a step that wait X seconds.
This is useful when you have asynchronous API and allows waiting before calling the next API in the scenario.

ParametersDescription
step_typepause
durationNumber of seconds to wait.
skippedIf true the step is skipped and nothing is running.

Example: Wait for 5 seconds

YAML
- step_type: pause
  duration: 5
JSON
{
  "step_type": "pause",
  "duration": 5
}

Request

request is the step who can call a REST Api.

ParametersDescription
step_typerequest
urlURL of your endpoint
methodHTTP verb of your request (GET, POST, PUT, DELETE, OPTIONS, PATCH)
bodyA string with the body of the request
variablesArray of variables to extract from the response (see Using Variables to Pass Data Between Steps for details)
headersObject who contains all the headers attach to the request (see how to add headers)
assertionsArray of assertions, this is the acceptance tests (see how to create assertion tests)
skippedIf true the step is skipped and nothing is running.

Headers

Headers are represented by an object containing all the headers to send.
Each header is has the name of the header for key and an array of strings as value.

You can use variables in the headers, they will be replaced before sending the request (see Using Variables to Pass Data Between Steps or Global Variables).

Example:

YAML
headers:
    Accept-Charset:
      - utf-8
    Accept: 
      - application/scim+json
    Authorization:
      - {{auth}}
JSON
{
  "headers": {
      "Accept-Charset": [
        "utf-8"
      ],
      "Accept": [
        "application/scim+json"
      ],
      "Authorization": [
        "{{auth}}"
      ]
    }
}

Assertions

Assertions are a big part of api-scenario, this is the acceptance tests of your request, it will allow you to simply write test to verify that you endpoint is doing what you want.

Assertion composition

PropertyDescription
sourceThe location of the data to extract for comparison.
See available source type to have authorized values.
comparisonThe type of operation to perform when comparing the extracted data with the target value. (see Available comparison type).
propertyThe property of the source data to retrieve.
  • For HTTP headers, this is the name of the header.
  • Data from a JSON / XML response body can be extracted by specifying the path of the data using standard JavaScript notation.
  • Unused for text content, status code, response time and response size.
valueThe expected value used to compare against the actual value.

Example:

YAML
- comparison: equals
  property: schemas
  value: User
  source: response_json
JSON
{
  "comparison": "equals",
  "property": "schemas",
  "value": "User",
  "source": "response_json"
}

Available source type

SourceConfig nameDescription
HTTP coderesponse_statusHTTP response status codes (https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
Response timeresponse_timeDuration of the request in seconds.
Response Headersresponse_headerTarget headers of the response.
Body response (JSON)response_jsonTarget the response body extract in JSON.
Body response (plain text)response_textTarget the response body extract in plain text.
Body response (XML)response_xml Target the response body extract in XML.

Available comparison type

ComparisonConfig nameDescription
is emptyemptyThe actual value exists and is an empty string or null.
is not emptynot_emptyThe actual value exists and is a value other than an empty string or null.
equalsequalA string comparison of the actual and expected value. Non-string values are cast to a string before comparing. For comparing non-integer numbers, use equals (number).
does not equalnot_equalA string comparison of the actual and target value.
containscontainsThe actual value contains the target value as a substring.
does not containdoes_not_containThe target value is not found within the actual value.
has keyhas_keyChecks for the existence of the expected value within a dictionary's keys. The actual value must point to a dictionary (JSON only).
has valuehas_valueChecks a list or dictionary for the existence of the expected value in any of the list or dictionary values. The actual value must point to a JSON list or dictionary (JSON only).
is nullis_nullChecks that a value for a given JSON or XML key is null.
is a numberis_a_numberValidates the actual value is (or can be cast to) a valid numeric value.
less thanis_less_thanValidates the actual value is (or can be cast to) a number less than the target value.
less than or equalis_less_than_or_equalsValidates the actual value is (or can be cast to) a number less than or equal to the target value.
greater thanis_greater_thanValidates the actual value is (or can be cast to) a number greater than the target value.
greater than or equalis_greater_than_or_equalValidates the actual value is (or can be cast to) a number greater than or equal to the target value.
equals (number)equal_numberValidates the actual value is (or can be cast to) a number equal to the target value. This setting performs a numeric comparison: for example, "1.000" would be considered equal to "1".

Request Chaining

Using Variables to Pass Data Between Steps

Request steps can define variables that extract data from HTTP responses returned when running the test. To create a variable, add a variables block to your step and specify the location of the data you'd like to extract from the response, and the name of this variable.

YAML
  variables:
    - source: response_json
      property: point
      name: active
JSON
{
  "variables": [
    {
      "source": "response_json",
      "property": "point",
      "name": "active"
    }
  ]
}

A variable is:

SourceThe location of the data to extract. Data can be extracted from
  • HTTP header values - response_header
  • Response bodies - response_json
  • Response status code - response_status
PropertyThe property of the source data to retrieve.
For HTTP headers this is the name of the header.
For JSON content, see below.
Unused status code.
Variable NameThe name of the variable to assign the extracted value to.
In subsequent requests you can retrieve the value of the variable by this name.
See Using Variables in Requests.

Extracting Data from JSON Body Content

Data from a JSON response body can be extracted by specifying the path of the target data using standard JavaScript notation. View sample JSON.

Global Variables

Some variables could be set up at launch, for that you can add options to the run command to pass it. Common values (base URLs, API tokens, etc.) that are shared across requests within a test, or tests within a bucket, should be stored in an Initial Variable. Once defined, the variable is available to all requests within the test.

To add a variable just use the option --variable or -V and specify the key:value of this variable.

./api-scenario run -F your_file.json --variable="baseUrl:http://www.google.com/" -V "token:token1234"

Note that if you create a variable in a step with the same name of a global variable it will override it.

Add / Override headers

Overriding headers works the same as global variables.
You can add a header for all your requests by using the option --header or -H, it will add or override the header for all requests.

./api-scenario run -F your_file.json --header="Content-Type:application/json" -H "Authorization: Bearer Token123"

Built-in Variables and Functions

Variable/FunctionDescriptionExample Output
{{timestamp}}Integer Unix timestamp (seconds elapsed since January 1, 1970 00:00 UTC)1384035195
{{utc_datetime}}UTC datetime string in ISO 8601 format.2013-11-07T19:24:41.418968
{{format_timestamp(value, format)}}Timestamp of the specified value in the specified format.
Any delimiters (e.g. -, /, ., *, etc.) can be used in the format with a combination of any of the following date/time format options. Also accepts variables. E.g. {{format_timestamp({{timestamp}}, YYYY-MM-DD)}}
  • YYYY - 4 digit year (e.g. 2019)
  • YYYY - 4 digit year (e.g. 2016)
  • YY - 2 digit year (e.g. 16)
  • MM - month
  • DD - day
  • HH - 24 hour (e.g. 13 == 1pm)
  • hh - 12 hour (e.g. 01 == 1pm)
  • mm - minutes
  • ss - seconds
2019-31-03
{{timestamp_offset(value)}}Integer Unix timestamp offset by the specified value in seconds (going back in time would be a negative offset value). Values should be passed without surrounding quotes.1383948795
{{random_int}}Random integer between 0 and 18446744073709551615407370955
{{random_int(a,b)}}Random integer value between a and b, inclusive.44674407370
{{random_string(length)}}Random alphanumeric string of the specified length (max 1000 characters).ddo1qlQR81
{{uuid}}Random universally unique identifier (UUID).99386c08-6da7-4833-bb31-e70ce747c921
{{encode_base64(value)}}Encodes value in Base64. Values should be passed without surrounding quotes. Also accepts variables e.g. {{encode_base64({{username}}:{{password}})}}dTpwDQo=
{{md5(value)}}Generate an MD5 hash based on value. Values should be passed without surrounding quotes. Also accepts variables e.g. {{md5({{timestamp}})}}50b7fe4da64720232c25bc7c6d66f6c5
{{sha1(value)}}Generate an SHA-1 hash based on value. Values should be passed without surrounding quotes. Also accepts variables e.g. {{sha1({{timestamp}})}}e0bd9304537cd8cb4e69ef5d73771fe218c484f5
{{sha256(value)}}Generate an SHA-256 hash based on value. Values should be passed without surrounding quotes. Also accepts variables e.g. {{sha1({{timestamp}})}}e3376ffb4b1e2c04b0fe68b52e8654696814b4883b47a56ff5a7df883725d8c1
{{hmac_sha1(value,key)}}Generate an HMAC using the SHA-1 hashing algorithm based on value and key. Values should be passed without surrounding quotes. Also accepts variables e.g. {{hmac_sha1({{timestamp}},key)}}163a04cd86a82b948a7e85f0ed3cd3b5929a7d0c
{{hmac_sha256(value,key)}}Generate an HMAC using the SHA-256 hashing algorithm based on value and key. Values should be passed without surrounding quotes. Also accepts variables e.g. {{hmac_sha1({{timestamp}},key)}}eb0b5c5b2a04ac25ff52c886e115f2e60c0dd8d50bab076dc065e95f5fd37fb9
{{url_encode(value)}}Create a percent-encoded string suitable for URL querystrings. This is not required for URL or form parameters defined in the request editor which are automatically encoded. Only use this if you need to double encode a value in a URL or include a URL encoded string in a header value.This%20is%20100%25%20URL%20encoded.

Using Variables in Requests

Once a variable has been defined, you can use it in any subsequent request.
Variables can be used in any request data field including the method, URL, header values, parameter values and request bodies.

To include the value of a variable in a request, enter the name of the variable surrounded by double braces e.g. {{variable_name}}.
If a variable is undefined when a request using that variable is executed, we will keep the variable un-replaced.

# Packages

No description provided by the author
No description provided by the author
No description provided by the author