{"id":16422328,"url":"https://github.com/stephenhillier/apitest","last_synced_at":"2025-10-04T20:34:49.824Z","repository":{"id":45791495,"uuid":"166175098","full_name":"stephenhillier/apitest","owner":"stephenhillier","description":"REST API testing tool for CLI, GitHub Actions, and Prometheus","archived":false,"fork":false,"pushed_at":"2019-11-07T07:51:14.000Z","size":724,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-01T00:44:15.422Z","etag":null,"topics":["api","github-actions"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stephenhillier.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-17T06:47:40.000Z","updated_at":"2023-12-28T14:20:17.000Z","dependencies_parsed_at":"2022-07-16T16:00:38.349Z","dependency_job_id":null,"html_url":"https://github.com/stephenhillier/apitest","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stephenhillier%2Fapitest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stephenhillier%2Fapitest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stephenhillier%2Fapitest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stephenhillier%2Fapitest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stephenhillier","download_url":"https://codeload.github.com/stephenhillier/apitest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238408428,"owners_count":19467090,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","github-actions"],"created_at":"2024-10-11T07:36:12.646Z","updated_at":"2025-10-04T20:34:44.791Z","avatar_url":"https://github.com/stephenhillier.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# apitest\nTest the behavior of an HTTP-based REST API [from the command line](#command-line), in a container based pipeline like [GitHub Actions](#github-actions), or as a running service with continuous monitoring and [Prometheus metrics](#prometheus-usage) collection.\n\n* [Defining test specs in YAML](#yaml-test-specs)\n  * [Complete example](#complete-example)\n* [Test specs syntax](#test-spec-properties)\n* [Logging in / retrieving tokens](#logging-in)\n* [jq style queries (for nested JSON)](#jq-style-json-parsing)\n* [Command line usage](#command-line)\n* [GitHub Actions usage](#github-actions)\n* [Prometheus usage](#prometheus-usage)\n\n## Usage\n\n### YAML test specs\n\nDefine requests in YAML.  [See the test spec properties](#test-spec-properties) for more details on individual fields.\n\n#### Complete example\n\n`apitest -e token=secret123 test.yaml`\n\n```yaml\n# test.yaml\nenvironment:\n  vars:\n    host: http://localhost:8000 # {{host}} will be replaced with this value\n  headers: # all requests will include headers defined here\n    Authorization: Bearer {{token}} # token was passed with the -e flag.\nrequests:\n  - name: Add a comment\n    url: \"{{host}}/comments\"\n    method: post\n    body: # this will be submitted as JSON in the request body\n      comment: This is my comment! \n    expect:\n      status: 201 # request will report as failed if 201 is not returned\n    set:\n      # set variables based on a field from the JSON response\n      - var: created_comment # {{created_comment}} will be set/updated for further requests to use\n        from: id\n  - name: Get single comment\n    url: \"{{host}}/comments/{{created_comment}}\"\n    method: get\n    expect:\n      status: 200  \n      values:\n        comment: This is my comment! # the JSON response `comment` field must match this value\n        date_posted:\n          exists: true # assertion rules can be used instead of a simple string value\n        num_replies:\n          gt: 0 # assert that num_replies is greater than 0\n```\n\n#### Test spec properties\n\n`environment`: define defaults like request headers or starting values of variables.\n\n  * `headers`: key/value pairs with any headers that should be added to each request.\n  * `vars`: variables that can be accessed through template tags; e.g. `host: example.com` will be available as `{{host}}` in request URLs.  Currently only URLs and headers will accept variables, and strings starting with `{{ }}` may need to be surrounded by quotes to make sure they are parsed as a string.\n\n```yaml\nenvironment:\n  vars:\n    host: https://www.example.com/api/v1\n    token: secret123\n  headers:\n    Authorization: Bearer {{token}}\n```\n\n`requests`: a list of requests to make as part of the test run.  Each request in the list can have the following properties:\n\n  * `name`: a name for your request\n  * `url`: the URL to make a request to\n  * `method`: HTTP method e.g. GET, POST\n  * `body`: key/value pairs that will be sent in the request body as JSON\n\n```yaml\nrequests:\n  - name: Add a joke\n    url: \"{{host}}/jokes\"\n    method: post\n    body:\n      joke: How did the Vikings send secret messages?\n      punchline: By norse code!\n```\n\n  * `expect`: add simple checks to an expect block:  \n    * `status`: HTTP status code  \n    * `values`: key/value pairs \n    * `strict`: use `strict: true` to require expect \u0026 response type to be exactly the same (e.g. the integer `10` is not equal to the string \"10\"). Default is `false`.\n\nKeys defined under `values` can use a basic comparison syntax (e.g. `type: Pepperoni`) or use an object block to add assertion rules:\n\n  * gt, lt: greater than, less than\n  * ge, le: greater than or equal to, less than or equal to\n  * equals: equality check. Note: not a strict comparison, so 123 is equivalent to \"123\" despite the type difference)\n  * exists: simple check that the key exists in the response body (use: `exists: true`). Note: \"exists: false\" currently not supported.\n\n```yaml\nrequests:\n  - name: Get pizza\n    url: \"{{host}}/pizzas/1\"\n    method: get\n    expect:\n      status: 200\n      values:\n        size: Large\n        type:\n          equals: Pepperoni # assertion rule syntax. Can also simply use \"type: Pepperoni\" for basic equality comparisons\n        quantity:\n          gt: 10 # greater than\n```\n\n  * `set`: a list of env variables to set from the response. Each item should have a `var` (the variable to be set) and `from` (a field in the response). This will be helpful for capturing the ID of a created resource to use in a later request.\n\n```yaml\nrequests:\n  - name: Order fast food\n    url: \"{{host}}/orders\"\n    method: post\n    body:\n      type: hamburder\n      quantity: 1000\n    set:\n      - var: created_order # can now use urls like example.com/api/orders/{{created_order}}\n        from: order_id\n```\n\n[See the full example](#complete-example) for more on how test specs can be defined using these properties.\n\n\n### Logging in\n\nYour first request can be to a token endpoint:\n\n```sh\napitest -f todos.yaml -e auth_id=$AUTH_ID -e auth_secret=$AUTH_SECRET\n```\n\n```yaml\nenvironment:\n  vars:\n    host: https://example.com\n    auth_url: https://example.com/oauth/token\n    auth_audience: https://example.com\n  headers:\n    Content-Type: application/json\n    Authorization: Bearer {{auth_token}}\nrequests:\n  - name: Log in\n    url: \"{{auth_url}}\"\n    method: post\n    body:\n      client_id: \"{{auth_id}}\"\n      client_secret: \"{{auth_secret}}\"\n      audience: \"{{auth_audience}}\"\n      grant_type: client_credentials\n    expect:\n      status: 200\n    set:\n      - var: auth_token # set the {{auth_token}} here\n        from: access_token\n  - name: Create todo as authenticated user\n    url: \"{{host}}/api/v1/todos\"\n    method: post\n    body:\n      title: \"Pick up groceries\"\n    expect:\n      status: 201\n```\n\n### jq style JSON parsing\n\nResponse body checking (the `expect` block) now supports jq style selectors:\n\n* `.foo` value at key\n* `.foo.bar` value at a nested key\n* `.foo.[0]` value at specified index of array\n\nFor convenience, the leading `.` can be omitted.\n\nExample:\n\n```yaml\n  - name: Get customer orders\n    url: \"{{host}}/api/v1/orders\"\n    method: get\n    expect:\n      values:\n        customer.name: Bill\n```\n\n### Command line\n\nExample: `apitest input.yaml`\n\nArguments:\n\n* `--file` `-f`: specify a file containing test specs. Example: `-f test/test.yaml`. Note: the file may be also be the first non-flag argument e.g. `apitest --monitor --delay=60 test.yaml`\n* `--env` `-e`: define variables for the test environment. Example: `-e myvar=test123`\n* `--test` `-t`: specify the name of a single test to run (use quotes if the name contains spaces). Example: `-t \"Todo list\"`\n* `--verbose` `-v`: verbose request \u0026 response logging.  Output is currently not pretty.\n\nThe following arguments apply to monitoring/metrics mode:\n* `--monitor` `-m`: enable monitoring mode (with metrics)\n* `--port` `-p`: port for metrics endpoint (monitoring mode only). The metrics endpoint is `/metrics`. Default: `2112`\n* `--delay` `-d`: delay (seconds) between automated test runs. Default: `300`\n\n### GitHub Actions\n\nAdd a step to your workflow like this:\n```\naction \"Run API tests\" {\n  uses = \"stephenhillier/apitest@master\"\n  args = [\"test/test.yaml\"]\n}\n```\n\nReplace `test/test.yaml` with the path to your yaml specs.\nSee the `.github/main.workflow` file in this repo for a working example.\n\n\n### Prometheus usage\n\napitest can be used with prometheus by setting the `--monitor` (or `-m`) flag.  `monitor` will\nset apitest to run in continuous mode, keeping track of request and error counts and request duration for every test.\n\nBy default, the metrics are available at `localhost:2112/metrics`. The port is configurable with `--listenPort`.\n\nThe following metrics are available with `hostname`, `path`, `method` and `name` (the test name in the yaml spec) labels:\n\n```\napitest_requests_duration\napitest_requests_duration_sum\napitest_requests_duration_count\napitest_requests_errors_total\napitest_requests_total\n```\n\n**Note**: the errors recorded denote assertion errors \u0026 tests that fail to run.  A request\nreturning status 500 would be considered successful if the test spec had expect `status: 500`.\n\n## Developing\n`go get github.com/stephenhillier/apitest`\n\n`go test`\n\n## Credits\n\n* https://github.com/savaki/jq - jq syntax\n* gopkg.in/yaml.v2 - yaml parsing\n* github.com/spf13/pflag\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstephenhillier%2Fapitest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstephenhillier%2Fapitest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstephenhillier%2Fapitest/lists"}