apitest

command module
v0.0.0-...-c25980c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 20, 2026 License: MIT Imports: 15 Imported by: 0

README

apitest

This is a simplified REST API tester. It accepts command line arguments and runs one or more tests located in a directory path provided on the command line.

The tests specify the HTTP method, URL path, parameters, headers, and request body if any. The request is made to the server defined in the URL, and the response is then validated against response info in the test file. This can include validating the HTTP status of the response as well as testing JSON elements of the response body.

Command line

The tools can be explicitly built and executed as a command line tool. It can also be made part of a project file and run by executed using the go run command. For example, the too might reside in a sub-directory of the tools directory of the project, and the test description files are then located in a directory tests within the source file directory.

An example invocation using go run might be

go run ./tools/apitest -d PASSWORD=zork tools/apitest/tests

or, if built as a command and available via the PATH,

apitest -d PASSWORD=zork tools/apitest/tests

The command line accepts the following options:

Option Value Description
--define, -x key=value Add an element to the substitution dictionary
--dictionary, -d file Add this dictionary file before running tests
--filter, -f string Only run tests whose file name contains the given string
--help, -h display help for the command
--rest, -r If present, display the REST request and response payloads
--verbose, -v If present, does more Verbose logging of progress

Note that you can specify an individual file instead of a directory if you wish to run only a single test. This won't look for default dictionary.json files so any symbol definitions will need to be done on the command line using the --define or --dictionary options.

Dictionary

A dictionary of key-value pairs is maintained during execution of the test. It can be initially populated using the dictionary.json file in the test directory, or by using the --define command line option to specify items to add to the dictionary.

In the test definition, a substitution can be made from the dictionary anywhere in the URL endpoint path, the parameter values, or the header values. These are identified using {{key}} notation, where key is the dictionary key. The value in the dictionary is substituted in the string with the then-current dictionary value.

Additionally, a test can specify that if the REST call is successful, then values can be extracted from the result and stored in the dictionary. For example, a REST call that logs in to the server and returns a token value can then store the token value in the dictionary to use in subsequent tests using the "Authenticate: Bearer token" header value.

Tests that fail do not update the dictionary.

Dictionary format

The dictionary.json file is a JSON object where each key is the dictionary key and it's value becomes a string value in the dictionary. For example,

{
    "SCHEME": "https",
    "HOST": "localhost"
}

This defines a dictionary with two items for {{SCHEME}} and {{HOST}} that can be substituted into strings in the test definitions. Note that a value defined on the command line will take precedence over a value found in the dictionary.json file.

The test framework automatically creates an item called {{HOST}} in the dictionary with the current system's FQDN name, which will be used if no host name is provided on the command line or in the dictionary.

It is a best-practice to avoid putting passwords anywhere in persisted data, and instead using the command line invocation to provide any password data needed for authentication in the test streams. By convention, this is named PASSWORD. See the example command line invocation above for an example of specifying a password of "zork".

Test Format

Here is an example test file. Below this is a discussion on the elements of the test object.


{
    "description": "Logon to the local server",
    "request": {
        "method": "POST",
        "endpoint": "/services/admin/logon",
        "parameters": {
            "kind":"auth1"
        },
        "body": "{ \"username\": \"{{USER}}\", \"{{PASSWORD}}\": \"password\" }",
        "headers": {
            "Content-Type": ["application/json"],
            "Accept": ["*/*"]
        }
    },
    "response": {
        "status": 200,
        "headers": {
            "Content-Type": "application/vnd.ego.logon+json"
        },
        "save": { "API_TOKEN": "token"}
    },
    "tests": [
        {
            "name": "api version",
            "query": "server.api",
            "value": "1"
        },
        {
            "name": "server id",
            "query": "server.id",
            "value": "{{SERVER_ID}}"
        }
    ]
}

The test object consists of four elements. Of these, only the request is required. The description is just a text description of the purpose of this test, and is displayed as part of Verbose logging when tests are run.

request object

The request object describes the request to be made to the server that constitutes the test to be run. It has the following fields:

Field Value Description
method string The HTTP method to use (GET, POST, etc)
endpoint string The URL endpoint including scheme, host, and path
body string If present, a text representation of the body send for PUT, POST, or UPDATE
parameters array If present, an array of "key":"value" objects which are added as parameters
headers key:array If present, an array of key values with an array of string values used as headers

If the endpoint starts with a "/" character, the scheme, host, and port are looked up in the dictionary using the keys "SCHEME", "HOST", and "PORT". If the "PORT" dictionary item does not exist, then no port is added to the URL and the default for the scheme is assumed. If the "HOST" key is not found in the dictionary, the name of the local machine is assumed. If there is no "SCHEME" key in the dictionary, "https" is the assumed scheme.

You can prevent the use of defaults by not having th endpoint start with a slash character; either specify your own dictionary substitution values or hardcode them into the test as appropriate. If your URL requires a username or username:password in the URL, then you must encode them yourself, either as literals in the test or as dictionary items.

response object

The response object indicates the required status value for the result of the HTTP call, along with any optional values that are extracted from the response body and stored in the dictionary.

IF the status value is greater than zero, then it must match the response value from the service. If headers are present, the header value must contain the given string. This allows for simple tests for json as example, or a more complete test like the one shown above for an entire media type specification application/vmd.ego.logon+json.

The notation for the item to save is a series of terms separated by "." characters.

If the item is only a single "." then it assumes the body is a single value (string, float, boolean, etc.) and that value is stored in the dictionary with the given name.

If the item is a single "*" then it means any matching values in an array. This can only be used with an array. It will continue the search for all matching fields following the dot and accumulates all possible values. For the save operation this only stores the first one found.

Otherwise, the part is expected to be either an object key name or a numeric index value. So in the example above, "server.id" means to use the value "id" that is located within the "server" object. You can specify a key that contains dots by escaping them. For example, foo.user\\.name looks first for a key called foo and within it a key called user.name. Note the use of \\. to escape a single dot in the key name.

tests object

The tests object is an array of objects, each one of which describes a test to be performed on the body of the response. Each has the following fields:

Field Description
name A descriptive string describing the test, used for logging
expression a "dot-notation" value describing the value's location in the response body
value The string value to be tested against the expression object
operation A string indicating the test type. If missing, "equal" is assumed

The notation for the item to validate is a series of terms separated by "." characters.

If the item is only a single "." then it assumes the body is a single value (string, float, boolean, etc.) and that value is stored in the dictionary with the given name.

If the item is a single "*" then it means any matching values in an array. This can only be used with an array. It will continue the search for all matching fields following the dot and accumulates all possible values. The only comparisons allowed with the "*" notation are equals and not-equals, as described below.

Otherwise, the part is expected to be either an object key name or a numeric index value. So in the example above, "server.id" means to use the value "id" that is located within the "server" object. You can specify a key that contains dots by escaping them. For example, foo.user\\.name looks first for a key called foo and within it a key called user.name. Note the use of \\. to escape a single dot in the key name.

The operation can be one of the following:

Operation Description
equals The value must match the expression object
not equals the value must not match the expression object
contains the expression object must contain the value string
not contains the expression object must not contain teh value string
gt The expression object is greater than the value string
ge The expression object is greater than or equal to the value string
lt The expression object is less than the value string
le The expression object is less than or equal to the value string
len The expression object must be an array whose length equals the number in the value string
exists The expression object must exist. There is no test against a value

Note that for relational tests (gt, le, etc) if both the expression object and the value string are representations of integer values, the comparison is done numerically. That is, "10" is greater than "2" numerically, but "10X" is less than "2X" because they aren't numeric values and so are compared as string values.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址