{"id":18812857,"url":"https://github.com/facundoolano/restpect","last_synced_at":"2025-06-15T04:35:13.294Z","repository":{"id":62434436,"uuid":"88091427","full_name":"facundoolano/restpect","owner":"facundoolano","description":"Succint and readable integration tests over RESTful APIs","archived":false,"fork":false,"pushed_at":"2017-08-22T20:10:18.000Z","size":199,"stargazers_count":88,"open_issues_count":1,"forks_count":3,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-12-08T12:13:11.587Z","etag":null,"topics":["http-requests","rest","rest-api","testing"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/facundoolano.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2017-04-12T20:02:36.000Z","updated_at":"2024-10-23T05:38:25.000Z","dependencies_parsed_at":"2022-11-01T21:01:39.318Z","dependency_job_id":null,"html_url":"https://github.com/facundoolano/restpect","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/facundoolano%2Frestpect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/facundoolano%2Frestpect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/facundoolano%2Frestpect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/facundoolano%2Frestpect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/facundoolano","download_url":"https://codeload.github.com/facundoolano/restpect/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230527875,"owners_count":18240052,"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":["http-requests","rest","rest-api","testing"],"created_at":"2024-11-07T23:35:24.406Z","updated_at":"2024-12-20T03:10:37.345Z","avatar_url":"https://github.com/facundoolano.png","language":"Clojure","readme":"# restpect\n\nRestpect is a small Clojure library that provides a set of functions to write succint and readable\nintegration tests over RESTful APIs.\n\n```clojure\n(require '[restpect.core :refer [created ok not-found]]\n         '[restpect.json :refer [GET PUT DELETE]]\n         '[clojure.test :refer [deftest]])\n\n(deftest create-get-and-delete-user\n  ;; expect 201 status response and body containing a :user-id integer\n  (created\n   (PUT \"http://example.com/api/v1/users/john\" {:email \"john@example.com\"})\n   {:user-id integer?})\n\n  ;; expect 200 status and body containing :user-id and :email\n  (ok\n   (GET \"http://example.com/api/v1/users/john\")\n   {:user-id integer?\n    :email   \"john@example.com\"})\n\n  ;; expect the response body to be a collection that contains at least one\n  ;; element that has a :user-id integer and the given :email\n  (ok\n   (GET \"http://example.com/api/v1/users/\")\n   #{{:user-id integer?\n      :email   \"john@example.com\"}})\n\n  (ok (DELETE \"http://example.com/api/v1/users/john\"))\n\n  ;; expect 404 status and a body with a :message string that contains \"not found\"\n  (not-found\n   (GET \"http://example.com/api/v1/users/john\")\n   {:message #\"not found\"}))\n```\n\n## Installation\n\nAdd the following to your project `:dependencies`:\n\n```clojure\n[restpect \"0.2.1\"]\n```\n\n## Reference\n### Request helpers\nThe `restpect.json` namespace provides wrappers around the [clj-http](https://github.com/dakrone/clj-http)\nrequest functions with sane defaults for JSON API requests (coerce request and\nresponse as JSON, don't throw exceptions on 4xx and 5xx responses, etc.).\n\nAll these functions have the following signature:\n\n``` clojure\n(POST url)\n(POST url body)\n(POST url body extras)\n```\n\nThe `body` is passed to clj-http as the `:form-params` for `POST`, `PUT`, `PATCH`\nand `DELETE`, and as the `:query-params` for `GET`, `HEAD` and `OPTIONS`.\n\n`extras` is a map of overrides passed to the clj-http call.\n\n### Assertion functions\n\n#### expect\nThe main assertion function is `restpect.core/expect`:\n\n``` clojure\n(expect response spec)\n```\n\nThe first argument (usually a clj-http response map, although it can be\nany value), will be compared against the given spec with the following criteria:\n\n* For maps, compare the value of each key in the spec with the value at the same key\nof the response, using `expect` recursively.\n* For sets, check that each element in the spec matches some element in the response,\ncomparing with `expect` recursively. This is useful to look for an element somewhere\nin a list, regardless of the position.\n* For other collections, compare each element in the spec with the same element at the\nsame position in the response, using `expect` recursively.\n* For functions, pass the value in the response to the spec function expecting a\ntruthy result.\n* For Regular expressions match the spec with the actual value (using [re-find](https://clojuredocs.org/clojure.core/re-find)).\n* For the rest of the values, expect the spec and the response values to be equal.\n\nExample:\n\n``` clojure\n(expect (GET url)\n        {:status 404\n         :body   [{:result  nil?\n                   :code    125\n                   :message #\"not found\"}]})\n```\n\nThis assertion is equivalent to the following:\n\n``` clojure\n(let [res (GET url)]\n  (is (= 404 (:status res)))\n  (is (nil? (get-in res [:body 0 :result])))\n  (is (= 125 (get-in res [:body 0 :code])))\n  (is (re-find #\"not found\" (get-in res [:body 0 :message]))))\n```\n\nAs seen in the example, `expect` is opinionated in the sense that it makes it\nsimple to test for values and conditions on specific fields of the reponses\nrather than doing an exact comparison of the payloads.\n\n#### Status shorthands\n`restpect.core` also provides a set of wrappers around `expect` with the\nnames of the different HTTP response status codes: `ok`, `created`, `bad-request`,\n`not-found`, etc.\n\nThese helpers implicitly validate the `:status` value of the given response map,\nand can optionally take a second argument that will be compared against the\nresponse body.\n\nUsing status shorthands, the example from the previous section becomes:\n\n``` clojure\n(not-found (GET url)\n           [{:result  nil?\n             :code    125\n             :message #\"not found\"}])\n```\n\nAnother example:\n\n``` clojure\n(deftest create-and-delete-user\n  (created (PUT \"http://example.com/api/v1/users/john\" {:email \"john@example.com\"}))\n  (ok (GET \"http://example.com/api/v1/users/john\"))\n  (ok (DELETE \"http://example.com/api/v1/users/john\"))\n  (not-found (GET \"http://example.com/api/v1/users/john\")))\n```\n\n### Test reporter\n\nRestpect also provides a custom test reporter that adds request and response\ninformation to failure messages (provided by `expect`) and does some formatting:\n\n![example report](report.png)\n\nThe report multimethod can be found in `restpect.report/report` and can be used\nwith plugins that allow to override the test reporter, like\n[eftest](https://github.com/weavejester/eftest)\nand [lein-test-refresh](https://github.com/jakemcc/lein-test-refresh):\n\n``` clojure\n;; project.clj\n:eftest {:report restpect.report/report}\n:test-refresh {:report restpect.report/report}\n```\n\nIf you already work with a custom reporter and just want to add some\nrequest/reponse data to its output, consider adding a defmethod for\n`:type :response`, for example:\n\n``` clojure\n(require '[restpect.report :refer [print-response]]\n         '[eftest.report.progress :as eftest]\n         '[clojure.test :refer [with-test-out]])\n\n(defmulti report :type)\n(defmethod report :default [m] (eftest/report m))\n(defmethod report :response [m]\n  (with-test-out\n    (println \"\\r\" (repeat 80 \" \") \"\\033[F\" \"\\033[F\")\n    (print-response (:response m))))\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffacundoolano%2Frestpect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffacundoolano%2Frestpect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffacundoolano%2Frestpect/lists"}