API Testing Steps

  • This is a list of all the steps that you can use in Zato API Test by default
  • A step is like a command in English that tells the tool how to build a request, how to invoke an endpoint or how to check individual fields in the responses that APIs under test return
  • You can also create your own steps in Python
  • Make sure that you read the API testing tutorial to understand how to use these steps in practice

Step types

There are three types of steps:

  • "Given" steps
  • "When" steps"
  • "Then" and "And" steps

Here's how they are related to each other:

  • "Given" steps describe the preconditions of a successful API invocation. That is, they let you specify what a correct request to invoke an endpoint. You'll be using "Given steps to populate JSON fields or to add HTTP headers and all of the "Given steps run before a "When" step runs.

  • "When" steps are the main point of interaction within a test. This is the moment when a remote endpoint is invoked using the request previously created with the test's "Given steps.

  • "Then" and "And" work the same and they always follow a "When" step. There always needs to be a "Then" step in a test, optionally followed by one or more "And" steps. They check the response that the API returns, possibly extracting some of its data for use in later tests.

The idea is to make your tests read like sentences in English.

"Given that I have this, given that I have that, when I do this, then that happens and another thing happens too".

Note that "Then" and "And" are the same steps and you can use any of them as the "Then" step. For instance, both of the examples below work the same.

Given address "https://example.com"
When the URL is invoked
Then status is "200"
And header "Server" is not empty
Given address "https://example.com"
When the URL is invoked
Then header "Server" is not empty
And status is "200"

Typically, you check the HTTP status first using "Then" and this is followed by "And" tests but sometimes there's no need to check the HTTP status code in which case you can put any other "And" step as the "Then" one.

Format of a step

  • Each step is a pattern, e.g. And header "{expected_header}" exists
  • You provide your values between quotation the marks, e.g. And header "Content-Type" exists
  • If you type in a step that doesn't match any pattern, its test will fail, e.g. And HTTP header "Content-Type" exists

Any value in any step, what you insert between quotation marks, can be data given directly in the test, or it can be a local variable created previously, a variable from config.ini or you can use environment variables.

Given address "https://example.com"
Given address "#server_address"
Given address "@server_address"
Given address "$Server_Address"

The remainder of this document is a list of patterns that apitest recognizes. Some of them are discussed in detail, with examples, whereas some are self-explanatory given that they are already in English.

If you want to add your own steps, check the advanced usage document.

↓ Given steps. Data format-independent. ↓

Given REST method "{method}"

  • What REST (HTTP) method to use when invoking an endpoint, e.g. GET, POST, DELETE etc.
Given REST method "POST"

Given address "{address}"

  • Used to provide the address of the endpoint to invoke
Given address "https://example.com"

Given URL path "{url_path}"

  • Used to provide URL path of the endpoint to invoke
Given URL path "/api/order/create"

Given query string "{query_string}"

  • Used to provide the query string of the URL path being invoked
Given query string "?order_type=new&order_state=submit"

Given format "{format}"

  • By default, all requests and responses are assumed to be in JSON
  • You can use "XML" and "RAW" to indicate that your requests and responses are XML or that they are raw data, without any specific data format, i.e. that it could be any string
  • If you don't provide this step explicitly, "JSON" will be used by default
Given REST method "XML"

Given request format "{format}"

  • Similar to the above but sets the data format of requests only. This is useful if requests and responses have a different data format, e.g. you send JSON requests but you get CSV in responses. CSV would be treated as the "RAW" data format.
Given request format "JSON"
Given response format "RAW"

Given response format "{format}"

  • As above, explicitly sets the data format of responses
  • Set it to "RAW" if you send JSON on input but you don't expect any response from the endpoint at all (as opposed to an empty one JSON response)

Given user agent is "{value}"

  • Sets a custom value of the "User-Agent" header which is useful if you need to impersonate a specific REST client
Given user agent is "System.Net.Http.HttpClient"

Given header "{header}" "{value}"

  • Set the value of an HTTP header
  • It can be used to specify API keys as well
Given header "Accept" "*/*"
Given header "X-My-Header" "MyValue"
Given header "X-API-Key" "Bearer F6jwCOe3bLO6FkqANMW7vHQtblwPZpVYCN4Rq6FPB7"

Given request is "{data}"

  • Explicitly provides the data to be sent to the endpoint
  • The initial value can be an empty request "{}", or it can be a JSON dict, e.g. "{"order_type":"new"}"
  • It's possible to provide an initial request using this step and enrich it with other steps, as in the example below
Given request is "{"client_type": "abc"}"
Given path "/client_id" in request is "123"

Given request "{request_path}"

  • Tells apitest to send to the endpoint under test a request from the specified path
  • This step is convenient to use if your requests are longer and you don't want to build them field-by-field
  • The request must already exist in the "request" directory of your apitest project
  • Each data format has its "request" directory in the project, e.g. JSON requests are in "json/request"
  • In the test, you only specify the file name that has the request and a prefix, such as "json/request" is prepended by apitest
  • For instance, if you have a request file under the path of "json/request/order.json", and your data format is JSON, you will only say "order.json" in your test, as below
Given request "order.json"

Given form field "{name}" is "{value}"

  • Used if you submit form data to an endpoint instead of JSON
Given request form field "order_type" is "new"

Given date format "{name}" "{format}"

  • Creates a named date format that can be reused in subsequent steps
Given date format "CRM_Date_Format" "MM-DD-YY""
Given path "/order/created" in request is a random date "CRM_Date_Format"
Given path "/order/shipped" in request is now "CRM_Date_Format"

Given I store "{value}" under "{name}"

  • Creates a new variable with the provided value
Given I store "123" under "max_timeout"
Given I store "abc" under "customer_type"

Given I store a random string under "{name}"

  • Generates a random string and creates a new variable with the resulting value
Given I store a random string under "order_type"

Given I store a random integer under "{name}"

  • Generates a random integer and creates a new variable with the resulting value
Given I store a random integer under "max_age"

Given I store a random float under "{name}"

  • Generates a random float and creates a new variable with the resulting value
Given I store a random float under "order_value"

Given I store a random date under "{name}", format "{format}"

  • Generates a random date, using the specific date format, and creates a new variable with the resulting value
Given I store a random date under "last_updated", format "CRM_Date_Format"

Given I encode "{value}" using Base64 under "{name}"

  • Encodes a given value using Base64 and creates a local variable with the resulting value
Given I encode "My_User_Name" using Base64 under "username"

↓ Given steps. JSON-specific. ↓

Given path "{path}" in request is "{value}"

  • Sets the path in the request to a specific value
Given path "/order/state" in request is "new"
Given path "/order/customer/type" in request is "#customer_type"
Given path "/order/customer/segment" in request is "@default_customer_segment"
Given path "/order/customer/market" in request is "@Default_Customer_Market"

Given path "{path}" in request is "{value}" (with literal_eval)

  • As above but lets you assign complex Python data to path rather than individual strings
  • Note that in the example below the data is a Python dict, not a JSON one
Given path "/order/customer" in request is "{'type':'new', 'segment':'abc'}"  (with literal_eval)

Given path "{path}" in request is an integer "{value}"

  • Sets the path in the request to a specific integer value
  • Note that by default all values are strings, which is why this step is needed if you want to use integers
Given path "/order/id" in request is an integer "123"

Given path "{path}" in request is a float "{value}"

  • As above but for float values
Given path "/order/geo_location" in request is a float "123.456"

Given path "{path}" in request is a list "{value}"

  • As above but for list values
Given path "/order/geo_location" in request is a list "[123,456,789]"

Given path "{path}" in request is True

  • Sets a given path in the outgoing request to True
Given path "/order/is_value" in request is True

Given path "{path}" in request is False

  • Sets a given path in the outgoing request to False
Given path "/order/is_value" in request is False

Given path "{path}" in request is a random string

  • Generates a random string and assigns it to the provided path
Given path "/customer/name" in request is a random string

Given path "{path}" in request is a random integer

  • As above, for integers
Given path "/order/id" in request is a random integer

Given path "{path}" in request is a random float

  • As above, for floats
Given path "/order/geo_location" in request is a random float

Given path "{path}" in request is a UUID4

  • As above, for UUID4 values
Given path "/customer/id" in request is a UUID4

Given path "{path}" in request is one of "{value}"

  • Randomly chooses one of the values from the list and assigns it to the path
Given path "/order/state" in request is one of "["new", "old", "shipped"]"

Given path "{path}" in request is a random date "{format}"

  • Generates a random date using the specified format and assigns it to the path
Given path "/order/creation_time" in request is a random date "CRM_Date_Format"

Given path "{path}" in request is now "{format}"

  • Assigns the current local time to the path using the specified format
Given path "/order/creation_time" in request is now "CRM_Date_Format"

Given path "{path}" in request is UTC now "{format}"

  • As above but uses UTC instead of the local time
Given path "/order/creation_time" in request is UTC now "CRM_Date_Format"

Given path "{path}" in request is UTC now "{format}" minus one hour

  • As above but subtracts one hour from the current time
Given path "/order/creation_time" in request is UTC now "CRM_Date_Format" minus one hour

Given path "{path}" in request is a random date after "{date_start}" "{format}"

  • Generates a random date using the given date format and starting after the given day
Given path "/order/creation_time" in request is a random date after "2024-11-19" "CRM_Date_Format"

Given path "{path}" in request is a random date before "{date_end}" "{format}"

  • As above but the generated date is prior to a given end date
Given path "/order/creation_time" in request is a random date before "2024-11-19" "CRM_Date_Format"

Given path "{path}" in request is a random date between "{date_start}" and "{date_end}" "{format}"

  • As above but the generated date is within a specific date range
Given path "{path}" in request is a random date between "2023-01-25" and "2029-03-27" "{format}"

↓ Then and And steps. Data format-independent. ↓

Then status is "{expected_status}"

  • Confirms that the HTTP status code is of a given value
When the URL is invoked
Then status is "200"

Then response is equal to that from "{path}"

  • Tells apitest to compare the response from the endpoint to the one saved in a file
  • This step is convenient to use if your responses are longer and you don't want to check them field-by-field
  • The response must already exist in the "response" directory of your apitest project
  • Each data format has its "response" directory in the project, e.g. JSON requests are in "json/response"
  • In the test, you only specify the file name that has the response and a prefix, such as "json/response" is prepended by apitest
  • For instance, if you have a response file under the path of "json/response/expected_order_response.json", and your data format is JSON, you will only say "expected_order_response.json" in your test, as below
When the URL is invoked
Then response is equal to that from "expected_order_response.json"

Then header "{expected_header}" is "{expected_value}"

  • Checks that an HTTP header has a specific value
Then header "X-MS-Workflow-Name" is "522d1a72-4a29-4f81-a03d-d4878a361223"

Then header "{expected_header}" is not "{expected_value}"

  • Checks that an HTTP header has any other value than the specified value
Then header "Content-Type" is not "text/xml"

Then header "{expected_header}" contains "{expected_value}"

  • Checks that an HTTP header has a specific substring
Then header "Content-Type" contains "json"

Then header "{expected_header}" does not contain "{expected_value}"

  • Checks that an HTTP doesn't have the substring specified
Then header "Content-Type" does not contain "xml"

Then header "{expected_header}" exists

  • Checks that the header exists, no matter if it's empty or if it has any value
Then header "Content-Type" exists

Then header "{expected_header}" does not exist

  • Checks that the header doesn't exist at all
Then header "X-Error" does not exist

Then header "{expected_header}" is empty

  • Checks that the header exists and it's empty
Then header "X-Error-Details" is empty

Then header "{expected_header}" is not empty

  • Checks that the header exists and has any other than empty
Then header "X-Status-Reason" is not empty

Then header "{expected_header}" starts with "{expected_value}"

  • Checks that the header has a given prefix
Then header "Content-Type" starts with "application"

Then header "{expected_header}" does not start with "{expected_value}"

  • Checks that the header doesn't have a given prefix
Then header "Content-Type" starts does not start with "text"

Then header "{expected_header}" ends with "{expected_value}"

  • Checks that the header has a given suffix
Then header "Content-Type" ends with "json"

Then header "{expected_header}" does not end with "{expected_value}"

  • Checks that the header doesn't have a given suffix
Then header "Content-Type" starts does not end with "xml"

Then I store "{path}" from response under "{name}"

  • Extracts a value from the response and stores it in a local variable under a specific name
  • Such variables can be later accessed from other tests by prefixing their names with the pound sign "#"
Then I store "/order/id" from response under "order_id"
Given path "/customer/order/last_id" in request is "#order_id"

Then I store "{path}" from response under "{name}", default "{default}"

  • As above but lets you specify a default value to use if the response doesn't have such a path
Then I store "/order/id" from response under "order_id", default "123"

Then variable "{variable}" is a list "{value}"

  • Checks that a previously extract local variable is a specific list
Then variable "#order_ids" is a list "[123, 456, 789]"

Then variable "{variable}" is an empty list

  • As above but checks that the list is empty
Then variable "#order_ids" is an empty list

Then variable "{variable}" is a string "{value}"

  • As above but checks that a variable is a string value
Then variable "#order_description" is a string "New order"

Then variable "{variable}" is an integer "{value}"

  • As above but checks that a variable is an integer value
Then variable "#order_id" is an integer "123"

Then variable "{variable}" is a float "{value}"

  • As above but checks that a variable is a float value
Then variable "#order_value" is a float "123.456"

Then variable "{variable}" is True

  • As above but checks that a variable is a boolean True
Then variable "#is_active" is True

Then variable "{variable}" is False

  • As above but checks that a variable is a boolean False
Then variable "#is_active" is False

Then I sleep for "{sleep_time}"

  • Pauses the test for that many seconds
  • This step is useful when you need to introduce a pause after you invoke an endpoint and before you can call another one
When the URL is invoked
Then I sleep for 5

Then context is cleaned up

  • Used to remove all the variables from the current context
  • This step is used to clean up at the end of tests so that variables don't carry over to other tests
When this scenario is invoked
Then context is cleaned up

Then form is cleaned up

  • Used to clear out the information about form submissions
  • This step is used to remove information about a form that was previously submitted before you submit a new one
When this scenario is invoked
Then form is cleaned up

↓ Then and And steps. JSON-specific. ↓

Then JSON response exists

  • Checks that any JSON response exists, no matter if it's empty or not
When the URL is invoked
Then JSON response exists

Then JSON response does not exist

  • Checks that a JSON response doesn't exist at all
When the URL is invoked
Then JSON response does not exist

Then JSON response is equal to "{expected}"

  • Checks that a JSON response is the same as given in the step
When the URL is invoked
Then JSON response is equal to "{"status:"ok", "created":true"}"

Then path "{path}" is "{value}"

  • Checks that the given path has a specific string value
When the URL is invoked
Then path "/status" is "ok"

Then path "{path}" is not a string "{value}"

  • The opposite of the above
When the URL is invoked
Then path "/status" is not a string "ok"

Then path "{path}" is "{value}" (with literal_eval)

  • As above but lets you use complex Python objects to compare the path with
  • Note that the value is a Python dict, not a JSON one
When the URL is invoked
Then path "/status" is "{'created':True, 'id':123}" (with literal_eval)

Then path "{path}" is JSON "{value}"

  • As above but lets use complex JSON objects to compare the path with
  • Note that the value is a JSON object, not a Python dict
When the URL is invoked
Then path "/status" is JSON "{"created":true, "id":123}"

Then path "{path}" is JSON equal to that from "{value}"

  • As above but the value for the comparison is loaded from a named file that needs to be in the "response" folder in your apitest project's directory
  • This step is useful if the JSON value used in the comparison is too big to embed it in the test directly or if the value needs to be reused in more tests
Then path "/status" is JSON equal to that from "expected_status.json"

Then path "{path}" is an integer "{value}"

  • Checks that the path has a value that is a specific integer
When the URL is invoked
Then path "/status/code" is "123"

Then path "{path}" is a float "{value}"

  • As above but for float numbers
When the URL is invoked
Then path "/order/geo_location" is "123.456"

Then path "{path}" is any string

  • Checks that the path has a value that is a string, no matter what specific value it is
When the URL is invoked
Then path "/customer/name" is any string

Then path "{path}" is a uuid

  • As above but for UUID4 values
When the URL is invoked
Then path "/status/code" is a uuid

Then path "{path}" is any integer

  • As above but for integers
When the URL is invoked
Then path "/status/code" is any integer

Then path "{path}" is any float

  • As above but for float numbers
When the URL is invoked
Then path "/order/geo_location" is any float

Then path "{path}" is any bool

  • As above but for boolean values
When the URL is invoked
Then path "/is_active" is any bool

Then path "{path}" is True

  • Checks that the path in response is a boolean True
When the URL is invoked
Then path "/is_active" is True

Then path "{path}" is False

  • Checks that the path in response is a boolean False
When the URL is invoked
Then path "/is_active" is False

Then path "{path}" is an empty dict

  • Checks that the path in response exists and that it's an empty dict
When the URL is invoked
Then path "/previous/details" is an empty dict

Then path "{path}" is empty

  • Checks that the path in response is a string that is empty
When the URL is invoked
Then path "/error_code" is empty

Then path "{path}" is null

  • Checks that the path in response is a JSON null object (None in Python)
When the URL is invoked
Then path "/error_details" is null

Then path "{path}" is not empty

  • Checks that the path in response has any value at all, no matter what it is
When the URL is invoked
Then path "/error_details" is not empty

Then path "{path}" is a list "{value}"

  • Checks that the path in response is a specific list
  • Values in the list use Python syntax, not JSON
When the URL is invoked
Then path "/previous/details" is a list "[123, None, 'abc', 'def']"

Then path "{path}" is an empty list

  • Checks that the path in response exists and that it's an empty list
When the URL is invoked
Then path "/previous/details" is an empty list

Then path "{path}" is not an empty list

  • Checks that the path in response exists, that it's a list with some elements but it doesn't matter what elements exactly
When the URL is invoked
Then path "/previous/details" is not an empty list

Then path "{path}" is one of "{value}"

  • Checks that the path in response has one of the provided values
When the URL is invoked
Then path "/status/details" is one of "["ok", "error", "invalid"]"

Then path "{path}" is not one of "{value}"

  • The opposite of the above
When the URL is invoked
Then path "/status/details" is not one of "["ok", "error", "invalid"]"

Then path "{path}" starts with "{value}"

  • Checks that the path in response has the specified prefix
When the URL is invoked
Then path "/status/details" starts with "err"

Then path "{path}" starts with any of "{value}"

  • Checks that the path in response with any of specified prefixes
When the URL is invoked
Then path "/status/details" starts with any of "["err_", "invalid_", "rejected_"]"

Then path "{path}" ends with "{value}"

  • Checks that the path in response has the specified suffix
When the URL is invoked
Then path "/status/details" ends with "_ok"

Then path "{path}" contains "{value}"

  • Checks that the path in response has the specified substring
When the URL is invoked
Then path "/status/details" contains "has been added"

Then path "{path}" contains data from "{value}"

  • Checks that the path in response contains a substring whose value is read from a file
  • The file to read the value from must be in the in the "response" folder in your apitest project's directory
When the URL is invoked
Then path "/status/details" contains data from "expected_substring.txt"

Then variable "{variable}" is any string

  • Checks that a given variable from the context is a string, no matter if it's an empty one or not
When the URL is invoked
Then variable "#username" is any string

Then variable "{variable}" is any list

  • As above but for lists
When the URL is invoked
Then variable "#order_history" is any list

Then variable "{variable}" is any dict

  • As above but for dicts
When the URL is invoked
Then variable "#order_details" is any dict

Then variable "{variable}" is any integer

  • As above but for integers
When the URL is invoked
Then variable "#order_id" is any integer

Then variable "{variable}" is any float

  • As above but for float numbers
When the URL is invoked
Then variable "#geo_latitude" is any float

Then variable "{variable}" is any UUID4

  • As above but for UUID4 numbers
When the URL is invoked
Then variable "#customer_id" is any UUID4

Then variable "{variable}" is any Boolean

  • Checks that a given variable from the context is a Boolean, no matter if it's True or False
When the URL is invoked
Then variable "#is_active" is any Boolean

When the URL is invoked

  • Invokes the endpoint configured in previous Given steps
Given address "http://apitest-demo.zato.io:8587"
Given URL path "/demo/rest"

When the URL is invoked
Then status is "200"

When this step is invoked

  • A pass-through step used only in clean-up scenarios
When this step is invoked
Then context is cleaned up
And form is cleaned up

When this scenario is invoked

  • An alias to "When this step is invoked" which works in exactly the same way. Added for convenience only.
When this scenario is invoked
Then context is cleaned up
And form is cleaned up

API key authentication

  • Export your API key to an environment variable and send it as an HTTP header
Given header "X-My-API-Key" "$MY_API_KEY"

Given Basic Auth "{username}" "{password}"

  • Lets you authenticate with the endpoint using Basic Auth
  • If your endpoint uses API keys, use Given header "{header}" "{value}" explained above
Given Basic Auth "@username" "$MY_PASSWORD"

Given OAuth2 endpoint "{address}"

  • Tells apitest what address to invoke to obtain an OAuth2 bearer token
Given OAuth2 endpoint "https://example.com/api/auth"

Given OAuth2 credentials "{username}" "{password}"

  • What username and password to use when obtaining an OAuth2 bearer token
Given OAuth2 credentials "@my_token_username" "$My_Token_Password"

Given OAuth2 scopes "{scopes}"

  • Optional scopes to request from the authentication endpoint
  • No scopes are sent by default
Given OAuth2 scopes "my.scope"

Given OAuth2 grant type "{grant_type}"

  • What grant type use with the authentication endpoint. It can be "password" or "client_credentials".
  • If this step is not used, the grant type will default to "password"
Given OAuth2 scopes "client_credentials"

Given OAuth2 extra fields "{extra_fields}"

  • Any extra key/value fields, such as OAuth2 audience, to send to the endpoint
  • If more than one field is to be sent, they need to separated by a comma
Given OAuth2 extra fields "audience=https://example.com, my_field=my_value"

Given OAuth2 client ID field "{client_id_field}"

  • The name of the field that contains the client ID in the request to the endpoint. It can be "username" or "client_id".
  • If this step is not used, the client ID field will default to "username"
Given OAuth2 client ID field "client_id"

Given OAuth2 client secret field "{client_secret_field}"

  • The name of the field that contains the client secret in the request to the endpoint. It can be "password" or "client_secret".
  • If this step is not used, the client secret field will default to "client_secret"
Given OAuth2 client secret field "client_secret"

Given OAuth2 request format "{request_format}"

  • In what format the request to the authentication server is sent. It can be "FORM" or "JSON" for form data or a JSON request
  • Which format to use will be provided in a given authentication endpoint's documentation
  • Note that servers always reply with a JSON response, but some of them require that you post a form as opposed to sending a JSON message in the request
  • If this step is not used, the request format field will default to "FORM"
Given OAuth2 request format "JSON"

Given I store an OAuth2 bearer token under "{name}"

  • This is the step that actually obtains a token from the authentication server pointed to by previous configuration steps
  • Once the token is obtained, it will be saved in a local variable pointed to by "name"
Given OAuth2 endpoint "@my_endpoint"
Given OAuth2 credentials "@my_token_username" "$My_Token_Password"
Given I store an OAuth2 form bearer token under "my_token"

Given OAuth2 bearer token "{token}"

  • Makes use of an already obtained token to invoke an API that requires bearer token authoentication
Given address "https://example.com"
Given URL path "/api"
Given REST method "GET"
Given OAuth2 bearer token "#my_token"
When the URL is invoked
Then status is "200"

Then OAuth2 context is cleaned up

  • This should be used to remove any OAuth2 configuration from the test's context
  • If you don't use this step, the OAuth2 configuration will propagate to other test files (test features), which is typically not desirable
Given address "https://example.com"
Given URL path "/api"
Given REST method "GET"
Given OAuth2 bearer token "#my_token"
When the URL is invoked
Then status is "200"
And OAuth2 context is cleaned up

Then context is cleaned up

  • Used to remove all the variables from the current context
  • This step is used to clean up at the end of tests so that variables don't carry over to other tests
When this scenario is invoked
Then context is cleaned up

Then form is cleaned up

  • Used to clear out the information about form submissions
  • This step is used to remove information about a form that was previously submitted before you submit a new one
When this scenario is invoked
Then form is cleaned up

Then OAuth2 context is cleaned up

  • This should be used to remove any OAuth2 configuration from the test's context
  • If you don't use this step, the OAuth2 configuration will propagate to other test files (test features), which is typically not desirable
Given address "https://example.com"
Given URL path "/api"
Given REST method "GET"
Given OAuth2 bearer token "#my_token"
When the URL is invoked
Then status is "200"
And OAuth2 context is cleaned up

Then context is logged

  • Logs all the local variables from the context on standard output
  • In the example below, it would log all everything related to OAuth2, including credentials and a token returned from the authentication endpoint
  • Note that you can also log all requests and response by using the --verbose flag, e.g. "apitest run /path/to/tests --verbose"
Given OAuth2 endpoint "@my_endpoint"
Given OAuth2 credentials "@my_token_username" "$My_Token_Password"
Given I store an OAuth2 form bearer token under "my_token"
Then context is logged

Read more