{"id":16208471,"url":"https://github.com/ehmicky/test-api","last_synced_at":"2025-10-30T09:12:55.374Z","repository":{"id":40569298,"uuid":"196992116","full_name":"ehmicky/test-api","owner":"ehmicky","description":"[WIP] Automated API testing","archived":false,"fork":false,"pushed_at":"2024-09-14T22:46:15.000Z","size":11672,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-10-11T10:17:02.419Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/ehmicky.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-15T12:13:27.000Z","updated_at":"2024-09-14T22:46:19.000Z","dependencies_parsed_at":"2024-04-22T20:27:42.200Z","dependency_job_id":"c5dab19d-2872-493d-b32f-d3cbbd8f5233","html_url":"https://github.com/ehmicky/test-api","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Ftest-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Ftest-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Ftest-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Ftest-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ehmicky","download_url":"https://codeload.github.com/ehmicky/test-api/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243976471,"owners_count":20377691,"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":[],"created_at":"2024-10-10T10:17:09.407Z","updated_at":"2025-10-30T09:12:55.365Z","avatar_url":"https://github.com/ehmicky.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Node](https://img.shields.io/badge/-Node.js-808080?logo=node.js\u0026colorA=404040\u0026logoColor=66cc33)](https://www.npmjs.com/package)\n[![Mastodon](https://img.shields.io/badge/-Mastodon-808080.svg?logo=mastodon\u0026colorA=404040\u0026logoColor=9590F9)](https://fosstodon.org/@ehmicky)\n[![Medium](https://img.shields.io/badge/-Medium-808080.svg?logo=medium\u0026colorA=404040)](https://medium.com/@ehmicky)\n\nAutomatic API testing.\n\n**This is a Work In Progress**: please do not use this at the moment, this\nrepository is not stable yet.\n\n# Features\n\n- **Declarative**. Tasks are specified in [simple YAML files](#tasks).\n- **Easy**. Each task is a single HTTP request/response. You only need to\n  specify the [request parameters](#http-requests) and the\n  [response validation](#response-validation). More complex requests flows\n  [are also supported](#sequences-of-requests).\n- Integrated to [**OpenAPI**](#openapi). Tasks re-use your OpenAPI specification\n  by default, making them less verbose and ensuring they match your\n  documentation.\n- **Fast**. Tasks have minimum overhead and run in parallel.\n- Nice **developer experience**. [Reporting](#example-output) is pretty,\n  informative and usable.\n- [**Data-driven testing**](#data-driven-testing) thanks to a simple\n  [templating system](#template-variables)\n- **Flexible**. Core functionalities can be extended with plugins.\n\n# Install\n\n```\nnpm install -D test-api\n```\n\nThis package works in Node.js \u003e=18.18.0.\n\nThis is an ES module. It must be loaded using\n[an `import` or `import()` statement](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c),\nnot `require()`. If TypeScript is used, it must be configured to\n[output ES modules](https://www.typescriptlang.org/docs/handbook/esm-node.html),\nnot CommonJS.\n\n# Usage (CLI)\n\n```\ntest-api\n```\n\nIf a task failed, exit code will be `1`.\n\nOptions are passed as CLI flags.\n\n```\ntest-api --merge.spec.definition openapi_schema.yml\n```\n\nTasks are passed as positional argument.\n\n```\ntest-api **/*.tasks.yml\n```\n\n# Usage (Node.js)\n\n```js\nimport { run } from 'test-api'\n\nawait run(options)\n```\n\nIf a task failed, `run()` will reject the promise with a `TestApiError`.\n\nOptions are passed as arguments. Tasks are passed as a `tasks` argument.\n\n```js\nawait run({\n  tasks: ['**/*.tasks.yml'],\n  merge: { spec: { definition: 'openapi_schema.yml' } },\n})\n```\n\n# Tasks\n\nTasks are specified in YAML or JSON files.\n\nBy default tasks at `**/*.tasks.yml|json` will be used.\n\nEach task file contains an array of tasks definitions.\n\nA single task performs the following:\n\n- sends an HTTP request to the API. The request parameters are specified using\n  the [`call` property](#http-requests).\n- validates the HTTP response according to the\n  [`validate` property](#response-validation).\n\nEach task must specify a `name` unique within its file.\n\n# Example input\n\n```yml\n- name: exampleTask\n  call:\n    method: GET\n    server: http://localhost:8081\n    path: /tags\n    query.onlyPublic: false\n  validate:\n    status: 200\n    body:\n      type: array\n      # Each tag object\n      items:\n        type: object\n        required: [tag, isPublic]\n        properties:\n          tag:\n            type: string\n            maxLength: 32\n          isPublic:\n            type: boolean\n\n# And so on\n- name: anotherTask\n```\n\nThis task calls:\n\n```http\nGET http://localhost:8081/icoTagNames?onlyPublic=false\n```\n\nIt then validates that:\n\n- the response status is `200`\n- the response body is an array of `{ tag: string, isPublic: boolean }`\n\n# Example output\n\nThis screenshot shows a typical task run with few task failures.\n\n![Screenshot](docs/screenshot.png)\n\nThe failed task is called `getLists.success` and performs the following HTTP\nrequest:\n\n```http\nGET http://127.0.0.1:8081/lists?accessToken=8ac7e235-3ad2-4b9a-8a22\n```\n\nIt expects a status code of 200 but receives 500 instead.\n\nOther tasks are shown failing at the end. A final summary is also present.\n\n# HTTP requests\n\nHTTP requests are specified with the `call` task property.\n\n```yml\n- name: taskName\n  call:\n    method: PUT\n    server: https://localhost:8081\n    path: /tags/:tagName\n    url.tagName: exampleTagName\n    query.accessToken: 1e42f0e1\n    headers.content-type: application/json\n    body:\n      _id: 1\n      name: exampleTagName\n      color: red\n    https:\n      rejectUnauthorized: false\n```\n\n- `method` `{string}` (default: `GET`): HTTP method\n- `server` `{string}`:\n  - server's origin (protocol + hostname + port)\n  - default values:\n    - protocol: `http://`\n    - hostname: environment variable `HOST` or (if absent) `localhost`\n    - port: environment variable `PORT` (if present)\n- `path` `{string}`: URL's path\n- `url.NAME` `{any}`:\n  - variable inside `server` or `path` using the `:NAME` notation\n  - for example if the `path` is `/tags/:tagName` it can be `url.tagName`\n  - the syntax is the same as\n    [Express route parameters](https://expressjs.com/en/guide/routing.html#route-parameters):\n    - `:NAME`: required parameter\n    - `:NAME?`: optional parameter\n    - `:NAME*`: several optional parameters\n    - `:NAME+`: several required parameters\n- `query.NAME` `{any}`:\n  - URL query variable\n  - specify a list delimited by `\u0026=` to use `NAME` several times\n    - e.g. `query.name: \"a\u0026=b\u0026=c\"` becomes the query variables\n      `?name=a\u0026name=b\u0026name=c`\n- `headers.NAME` `{any}`:\n  - HTTP request header\n  - case insensitive\n  - `headers.content-type` defaults to:\n    - `application/json` if `body` is an object or an array\n    - `application/octet-stream` otherwise\n- `body` `{any}`: request body\n- `https` `{object}`:\n  - HTTPS/TLS options\n  - Same as the ones allowed by\n    [https.request()](https://nodejs.org/api/https.html#https_https_request_options_callback),\n    i.e. `ca`, `cert`, `ciphers`, `clientCertEngine`, `crl`, `dhparam`,\n    `ecdhCurve`, `honorCipherOrder`, `key`, `passphrase`, `pfx`,\n    `rejectUnauthorized`, `secureOptions`, `secureProtocol`, `servername`,\n    `sessionIdContext`.\n\n`url.NAME`, `query.NAME`, `headers.NAME` and `body` can be either a string or\nany other JSON type:\n\n- they will be serialized according to the HTTP request header `Content-Type`\n- however at the moment only JSON is supported. Notably `multipart/form-data`\n  and `x-www-form-urlencoded` are not supported yet.\n- same goes for the response headers and body\n\n# Response validation\n\nThe HTTP response is validated against the `validate` task property.\n\n```yml\n- name: taskName\n  validate:\n    status: 201\n    headers.content-type: application/json\n    body:\n      type: array\n```\n\n- `status` `{string|integer}`:\n  - expected HTTP status code\n  - can be:\n    - a specific status code like `201`\n    - a range like `1xx`, `2xx`, `3xx`, `4xx` or `5xx`\n    - a space-delimited list of these like `201 202 3xx`\n  - default: `2xx`\n- `headers.NAME` `{any|jsonSchema}`:\n  - expected value for this HTTP response header\n  - `NAME` is case-insensitive\n  - this can be either:\n    - any value checked for equality\n    - a\n      [JSON schema version 4](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject)\n      with the additional following properties:\n      - `x-optional` `{boolean}` (default: `true`): if `false`, validate that\n        the HTTP header is present in the response\n      - `x-forbidden` `{boolean}` (default: `false`): if `true`, validate that\n        the HTTP header is not present in the response\n- `body` `{any|jsonSchema}`:\n  - expected value for the response body\n  - this can be either a non-object checked for equality or a\n    [JSON schema version 4](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject)\n    (like `headers.NAME`)\n\nValidation can also vary according to the response's status code by using the\nfollowing notation.\n\n```yml\n- name: taskName\n  validate:\n    201:\n      body:\n        type: array\n    400:\n      body:\n        type: object\n```\n\n# OpenAPI\n\nThe `call` and `validate` tasks properties can be pre-filled if you have\ndescribed your API endpoints with [OpenAPI](https://www.openapis.org/).\n\n```yml\n- name: taskName\n  spec:\n    operation: getTags\n    definition: ../openapi_document.yml\n```\n\n- `operation` `{string}`: OpenAPI's\n  [`operationId`](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object)\n- `definition` `{string}`:\n  - path to the OpenAPI document\n  - it is likely that the same OpenAPI document is re-used across tasks, so the\n    [`merge` task property](#shared-properties) can be used\n  - the OpenAPI document syntax is validated\n  - only OpenAPI 2.0 is currently supported but we plan to add OpenAPI 3.0\n    support\n\nThe following OpenAPI properties are currently used:\n\n- the\n  [`consumes` OpenAPI property](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object)\n  sets the request `Content-Type` header (`call['headers.content-type']`)\n- the\n  [`produces` OpenAPI property](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object)\n  sets the request `Accept` header (`call['headers.accept']`) and validate the\n  response's `Content-Type` header (`validate['headers.content-type']`)\n- the\n  [`host` and `basePath` OpenAPI properties](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#swagger-object)\n  set the `call.server` task property. At the moment the protocol is always\n  `http://`.\n- the `call.method` and `call.path` is taken from the\n  [OpenAPI definition](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#path-item-object)\n- the request parameters are randomly generated from the\n  [`parameters` OpenAPI property](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameterObject):\n  - the random generation is based on\n    [JSON schema faker](https://github.com/json-schema-faker/json-schema-faker)\n  - OpenAPI parameters not marked as `required` will only be used (and merged)\n    if they are explicitly present in the `call` task property\n  - the following special values can used in the `call` task property:\n    - `valid`: re-use the OpenAPI parameter definition. Useful if the OpenAPI\n      parameter is not marked as `required`. Redundant otherwise.\n    - `undefined`: do not use the OpenAPI parameter definition\n- the\n  [response's](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#responseObject)\n  `schema` and `headers` OpenAPI properties are used to validate the HTTP\n  response (`validate.status|body|headers`)\n\nOpenAPI schemas can use the following extensions:\n\n- `schema.x-nullable|oneOf|anyOf|not`: behaves like OpenAPI 3.0\n  `nullable|oneOf|anyOf|not`\n- `schema.x-additionalItems|dependencies`: behaves like JSON schemas\n  `additionalItems|dependencies`\n\n# Shared properties\n\nTo specify properties shared by all tasks, use the `merge` option:\n\n```\ntest-api --merge.spec.definition ../openapi_document.yml\n```\n\nTo specify properties shared by a few tasks, create a task with a `merge`\nproperty.\n\n```yml\n- name: sharedTask\n  merge: invalidCheck/.*\n  validate:\n    status: 400\n```\n\nThe `merge` property should be a regular expression (or an array of them)\ntargeting other tasks by `name`. The shared task will not be run. Instead it\nwill be deeply merged to the target tasks.\n\nThe target tasks can override the shared task by using `undefined` inside task\nproperties.\n\n# Template variables\n\nTemplate variables can be used using the `$$name` notation.\n\nTemplate variables are specified using the `template` task property.\n\n```yml\n- name: exampleTask\n  template:\n    $$exampleVariable: true\n  call:\n    query.isPublic: $$exampleVariable\n```\n\nThe example above will be compiled to:\n\n```yml\n- name: exampleTask\n  call:\n    query.isPublic: true\n```\n\nTemplate variables can:\n\n- be concatenated within a string like `$$exampleVariable --- $$anotherVariable`\n- use brackets and dots to access object properties and array indexes like\n  `$$exampleArray[0].propertyName`\n- be functions:\n  - by default they are triggered with no arguments\n  - to specify arguments one can use the following notation:\n    `{ $$exampleFunction: [firstArg, secondArg] }`\n\nThe following template variables are always available:\n\n- `$$env`: use environment variables (case-sensitive)\n- `$$random`: generate fake data using a\n  [JSON schema version 4](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject)\n- `$$faker`: generate fake data using\n  [Faker.js](https://github.com/tzuryby/Faker.js)\n\n```yml\n- name: exampleTask\n  call:\n    server: $$env.SERVER\n    query.password:\n      $$random:\n        type: string\n        minLength: 12\n        pattern: '[a-zA-Z0-9]'\n    body:\n      name: $$faker.name.firstName\n```\n\n# Sequences of requests\n\nA request can save its response using `variables`. Other requests will be able\nto re-use it as template variables. This creates sequences of requests.\n\n```yml\n- name: createAccessToken\n  variables:\n    $$accessToken: call.response.body.accessToken\n\n- name: taskName\n  call:\n    query.accessToken: $$accessToken\n```\n\nThe `call.request` and `call.response` are available to re-use the HTTP request\nand response.\n\nThe task will fail if the variable is `undefined` unless you append the word\n`optional` to its value.\n\n```yml\n- name: createAccessToken\n  variables:\n    $$accessToken: call.response.body.accessToken optional\n```\n\n# Tasks selection\n\nBy default all tasks are run in parallel at the same time.\n\nTo only run a few tasks use the `only` option.\n\n```\ntest-api --only 'taskNameRegularExpression/.*'\n```\n\nOr the `only` task property.\n\n```yml\n- name: taskName\n  only: true\n```\n\nThe `skip` option and task property can be used to do the opposite.\n\n# Reporting\n\nThe following reporters are available:\n\n- `pretty`: default reporter\n- `tap`: [Test Anything Protocol](https://testanything.org/)\n- `notify`: desktop notification\n- `data`: JSON output\n\nSpecify the `--report.REPORTER` option to select which reporter to use\n\n```\ntest-api --report.notify --report.pretty\n```\n\nUse the `--report.REPORTER.output` to redirect the output of a reporter to a\nfile:\n\n```\ntest-api --report.pretty.output path/to/file.txt\n```\n\nUse the `--report.REPORTER.level` to modify the verbosity:\n\n```\ntest-api --report.pretty.level info\n```\n\nThe available levels are:\n\n- `silent`\n- `error`\n- `warn` (default for `pretty`)\n- `info` (default for `tap` and `notify`)\n- `debug` (default for `data`)\n\n# Data-driven testing\n\nWith the `repeat.data` task property, tasks are repeated by iterating over an\narray of inputs.\n\n```yml\n- name: exampleTask\n  repeat:\n    data:\n      - name: apples\n        quantity: 1\n      - name: oranges\n        quantity: 10\n      - name: plums\n        quantity: 100\n  call:\n    method: GET\n    path: /fruits/:fruitName\n    url.fruitName: $$data.name\n    query:\n      quantity: $$data.quantity\n```\n\nThe task above will be run three times: `GET /fruits/apples?quantity=1`,\n`GET /fruits/oranges?quantity=10` and `GET /fruits/plums?quantity=100`.\n\nWith the `repeat.times` task property, tasks are simply repeated as is. This can\nbe useful when used with the `$$random` template function.\n\n```yml\n- name: exampleTask\n  repeat:\n    times: 10\n```\n\n`repeat.data` and `repeat.times` can be combined.\n\n# Support\n\nFor any question, _don't hesitate_ to [submit an issue on GitHub](../../issues).\n\nEveryone is welcome regardless of personal background. We enforce a\n[Code of conduct](CODE_OF_CONDUCT.md) in order to promote a positive and\ninclusive environment.\n\n# Contributing\n\nThis project was made with ❤️. The simplest way to give back is by starring and\nsharing it online.\n\nIf the documentation is unclear or has a typo, please click on the page's `Edit`\nbutton (pencil icon) and suggest a correction.\n\nIf you would like to help us fix a bug or add a new feature, please check our\n[guidelines](CONTRIBUTING.md). Pull requests are welcome!\n\n\u003c!-- Thanks go to our wonderful contributors: --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START --\u003e\n\u003c!-- prettier-ignore --\u003e\n\u003c!--\n\u003ctable\u003e\u003ctr\u003e\u003ctd align=\"center\"\u003e\u003ca href=\"https://fosstodon.org/@ehmicky\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/8136211?v=4\" width=\"100px;\" alt=\"ehmicky\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eehmicky\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ehmicky/test-api/commits?author=ehmicky\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#design-ehmicky\" title=\"Design\"\u003e🎨\u003c/a\u003e \u003ca href=\"#ideas-ehmicky\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e \u003ca href=\"https://github.com/ehmicky/test-api/commits?author=ehmicky\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n--\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehmicky%2Ftest-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fehmicky%2Ftest-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehmicky%2Ftest-api/lists"}