{"id":15733582,"url":"https://github.com/tomwright/apitestr","last_synced_at":"2025-03-31T03:45:32.260Z","repository":{"id":57607433,"uuid":"156787263","full_name":"TomWright/apitestr","owner":"TomWright","description":"API testing framework based on JSON test files, written in Go.","archived":false,"fork":false,"pushed_at":"2020-03-26T12:09:26.000Z","size":54,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-06T08:28:48.628Z","etag":null,"topics":["api","api-test","api-tester","api-testing","api-testing-framework","api-tests","apis","gjson","go","golang","test-file","test-files","testing","testing-tool","testing-tools"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TomWright.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.MD","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-09T00:32:05.000Z","updated_at":"2020-03-26T12:08:40.000Z","dependencies_parsed_at":"2022-08-30T05:23:16.683Z","dependency_job_id":null,"html_url":"https://github.com/TomWright/apitestr","commit_stats":null,"previous_names":["tomwright/api-testr"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomWright%2Fapitestr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomWright%2Fapitestr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomWright%2Fapitestr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomWright%2Fapitestr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TomWright","download_url":"https://codeload.github.com/TomWright/apitestr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246413242,"owners_count":20773053,"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","api-test","api-tester","api-testing","api-testing-framework","api-tests","apis","gjson","go","golang","test-file","test-files","testing","testing-tool","testing-tools"],"created_at":"2024-10-04T01:00:34.435Z","updated_at":"2025-03-31T03:45:32.242Z","avatar_url":"https://github.com/TomWright.png","language":"Go","readme":"# apitestr\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/TomWright/apitestr)](https://goreportcard.com/report/github.com/TomWright/apitestr)\n[![Documentation](https://godoc.org/github.com/TomWright/apitestr?status.svg)](https://godoc.org/github.com/TomWright/apitestr)\n![Test](https://github.com/TomWright/apitestr/workflows/Test/badge.svg)\n![Build](https://github.com/TomWright/apitestr/workflows/Build/badge.svg)\n\nA package used to run API tests defined in JSON files.\n\n## Usage\n\n### Install\nDownload an executable from the [latest release](https://github.com/TomWright/apitestr/releases/latest).\nYou may have to `chmod +x` the download file.\n\nOr build from source:\n```\ngit clone git@github.com:TomWright/apitestr.git\ncd apitestr\ngo build -o apitestr cmd/app/main.go\n```\n\n### Run the tests\n```\napitestr -tests ./tests -base http://localhost:8080\n```\n\n## Tests\nTests are contained in a single JSON file - [Example test here](tests/example.json).\n\nA test belongs to a single group and can be ordered within that group.\n\nTests will execute a single request whose response is then validated by a list of checks. \n\n### Groups\n\nTests are executed group by group.\n\nYou can add a test to a group using the `group` JSON key. If no group is provided then `default` is used.\n\n### Order\n\nIf you need your tests executed in a specific order you can use the `order` JSON key. If no order is provided then `0` is used.\n\nTests with the same group and order will be run at the same time.\n\n## Running Tests\n\n### Running a single test\n```\n// a context is always required\nctx := context.Background()\n\n// set the base url to be used with the test\nctx = testr.ContextWithBaseURL(ctx, \"https://example.com\")\n\n// parse the test file\nt, err := parse.File(ctx, \"path/to/my/test.json\")\nif err != nil {\n    panic(err)\n}\n\n// run the test\nerr := testr.Run(t, nil)\n\n// handle the error if the test failed\nif err != nil {\n    panic(fmt.Errorf(\"test `%s` failed: %s\\n\", t.Name, err))\n}\n```\n\n### Running groups of tests\n```\n// a context is always required\nctx := context.Background()\n\n// set the base url to be used with the test\nctx = testr.ContextWithBaseURL(ctx, \"https://example.com\")\n\n// parse the test files\ntests := make([]*testr.Test, 2)\nvar err error\ntests[0], err = parse.File(ctx, \"path/to/my/test1.json\")\nif err != nil {\n    panic(err)\n}\ntests[1], err = parse.File(ctx, \"path/to/my/test1.json\")\nif err != nil {\n    panic(err)\n}\n\nres := testr.RunAll(testr.RunAllArgs{}, tests...)\n\n// log the results\nlog.Printf(\"tests finished\\n\\texecuted: %d\\n\\tpassed: %d\\n\\tfailed: %d\", res.Executed, res.Passed, res.Failed)\n```\n\n## Checks\n\nChecks are how you validate that the response returned is correct.\n\n### Body Equal\nChecks that the body returned is exactly equal to the value given.\n```\n{\n  \"type\": \"bodyEqual\",\n  \"data\": {\n    \"value\": \"OK\"\n  }\n}\n```\n\n### JSON Body Equal\nChecks that the body returned matches the given JSON object.\n```\n{\n  \"type\": \"jsonBodyEqual\",\n  \"data\": {\n    \"value\": {\n      \"userId\": 1,\n      \"id\": 1,\n      \"title\": \"delectus aut autem\",\n      \"completed\": false\n    }\n  }\n}\n```\n\n### JSON Body Query Exists\nQueries the JSON body using [gjson](https://github.com/tidwall/gjson) and ensures that the queried element exists.\n```\n{\n  \"type\": \"jsonBodyQueryExists\",\n  \"data\": {\n    \"query\": \"title\"\n  }\n}\n```\n\nThere is an optional `dataId` property you can set in the data object of this check. If this property is not empty, the value found by this check will be stored under the given `dataId` for use by subsequent tests.\n\n### JSON Body Query Equal\nQueries the JSON body using [gjson](https://github.com/tidwall/gjson) and ensures that the queried element has a value equal to the one specified.\n```\n{\n  \"type\": \"jsonBodyQueryEqual\",\n  \"data\": {\n    \"query\": \"title\",\n    \"value\": \"delectus aut autem\"\n  }\n}\n```\n\nThere is an optional `dataId` property you can set in the data object of this check. If this property is not empty, the value found by this check will be stored under the given `dataId` for use by subsequent tests.\n\n### JSON Body Query Regex Match\nQueries the JSON body using [gjson](https://github.com/tidwall/gjson) and ensures that the queried element matches the given regex pattern.\n```\n{\n  \"type\": \"jsonBodyQueryRegexMatch\",\n  \"data\": {\n    \"query\": \"title\",\n    \"pattern\": \"([a-z]{8}) ([a-z]{3}) ([a-z]{5})\"\n  }\n}\n```\n\nThere is an optional `dataIds` property you can set in the data object of this check. If this property is not empty, the values found in matching groups by this check will be stored under the given `dataIds` for use by subsequent tests.\n\nE.g.\nThis response:\n```\n{\n    \"message\": \"Hello there, Tom\"\n}\n```\nWith this check:\n```\n{\n  \"type\": \"jsonBodyQueryRegexMatch\",\n  \"data\": {\n    \"query\": \"message\",\n    \"pattern\": \"([a-zA-Z]+), ([a-zA-Z]+)\",\n    \"dataIds\": {\n        \"0\": \"responseMessage\",\n        \"1\": \"responseGreeting\",\n        \"2\": \"responseName\"\n    }\n  }\n}\n```\nWill result in the following variables being available for use later on:\n- `$.responseMessage`: `Hello there, Tom`\n- `$.responseGreeting`: `Hello there`\n- `$.responseName`: `Tom`\n\n### Status Code Equal\nChecks that the status code returned matches the given value.\n```\n{\n  \"type\": \"statusCodeEqual\",\n  \"data\": {\n    \"value\": 200\n  }\n}\n```\n\n### Custom Body Check\nReads the response body into a byte array and provides it to your custom function to validate, identified by the `id` value.\n```\n{\n  \"type\": \"bodyCustom\",\n  \"data\": {\n    \"id\": \"123check\"\n  }\n}\n```\n\n#### Creating your custom check\nFirst create your custom validator func - this *must* implement the `check.BodyCustomCheckerFunc` interface.\n```\nvar custom123 check.BodyCustomCheckerFunc = func(bytes []byte) error {\n    if string(bytes) != \"[1,2,3]\" {\n        return fmt.Errorf(\"response is not 1,2,3\")\n    }\n    return nil\n}\n```\n\nThen register the custom func against your context.\n```\nctx = testr.ContextWithCustomBodyCheck(ctx, \"123check\", custom123)\n```\n\nThen simply use the id of `123check` in your `bodyCustom` check data.\n\n## Request Initialisation\nSometimes you'll need to do some dynamic testing, and that's where request init functions come in.\n\nRegister your init funcs as follows:\n```\nvar myInitFunc RequestInitFunc = func(ctx context.Context, req *http.Request, data map[string]interface{}) (*http.Request, error) {\n    // modify or create a new request here\n    // use the data map as required\n    return req, nil\n}\ntestr.ContextWithRequestInitFunc(ctx, \"my-init-func-id\", myInitFunc)\n```\n\nAnd then use them in your tests as so:\n```\n{\n    \"request\": {\n        \"init\": {\n            \"my-init-func-id\": {\n                \"some\": \"data\",\n            }\n        }\n    }\n}\n```\n\n### Common init funcs\nSome common init funcs are provided.\n\n#### Request replacements\nThe replacements init func allows you to replace placeholders in the request URL path, URL query, headers and body with a given value.\n\nIt can be registered as follows:\n```\ntestr.ContextWithRequestInitFunc(ctx, \"replacements\", testr.RequestReplacements)\n```\n\nUsed in tests as so:\n```\n{\n    \"request\": {\n        \"init\": {\n            \"replacements\": {\n                \":name:\": \"Tom\",\n            }\n        },\n        \"url\": \"https://example.com/users?name=:name:\"\n    }\n}\n```\n\nIf you want to use a data value that has been stored in the context by another test you should use `$.my-data-item` as the replacement value, where the previous test had used `my-data-item` as the `dataId`.\nOr, moving on from the example given previously in *JSON Body Query Regex Match* you would do something like this:\n```\n{\n    \"request\": {\n        \"init\": {\n            \"replacements\": {\n                \":name:\": \"$.responseName\",\n            }\n        },\n        \"url\": \"https://example.com/users?name=:name:\"\n    }\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomwright%2Fapitestr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomwright%2Fapitestr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomwright%2Fapitestr/lists"}