{"id":32181993,"url":"https://github.com/ccfontes/eg","last_synced_at":"2025-10-21T22:56:56.675Z","repository":{"id":62432580,"uuid":"185126097","full_name":"ccfontes/eg","owner":"ccfontes","description":"eg delivers clojure.test function tests with conciseness","archived":false,"fork":false,"pushed_at":"2023-08-16T15:57:55.000Z","size":266,"stargazers_count":24,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-10-21T22:56:42.845Z","etag":null,"topics":["babashka","clojure","clojurescript","tests"],"latest_commit_sha":null,"homepage":"","language":"Clojure","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/ccfontes.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":"2019-05-06T04:57:18.000Z","updated_at":"2023-08-20T01:37:21.000Z","dependencies_parsed_at":"2022-11-01T21:15:30.581Z","dependency_job_id":null,"html_url":"https://github.com/ccfontes/eg","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ccfontes/eg","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccfontes%2Feg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccfontes%2Feg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccfontes%2Feg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccfontes%2Feg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ccfontes","download_url":"https://codeload.github.com/ccfontes/eg/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccfontes%2Feg/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280348058,"owners_count":26315367,"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","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["babashka","clojure","clojurescript","tests"],"created_at":"2025-10-21T22:56:55.665Z","updated_at":"2025-10-21T22:56:56.664Z","avatar_url":"https://github.com/ccfontes.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# eg [![Clojars Project](https://img.shields.io/clojars/v/eg.svg)](https://clojars.org/eg) [![CI](https://github.com/ccfontes/eg/actions/workflows/checks.yml/badge.svg)](https://github.com/ccfontes/eg/actions/workflows/checks.yml) [![codecov](https://codecov.io/gh/ccfontes/eg/branch/master/graph/badge.svg?token=dUSRsSBOAW)](https://codecov.io/gh/ccfontes/eg) [![bb compatible](https://raw.githubusercontent.com/babashka/babashka/master/logo/badge.svg)](https://babashka.org) [![Universal Permissive License v1.0](https://img.shields.io/badge/license-MIT-black.svg)](LICENSE.md)\n\n```clj\n(deftest inc-test\n  (is (= 1 (inc 0))))\n```\nin *eg* becomes:\n```clj\n(eg inc 0 1)\n```\n\n*eg* supports Clojure, Babashka, ClojureScript JVM, and ClojureScript JS, the same as clojure.test.\n\nVanilla `clojure.test` and *eg* test results are included in the same report.\nAlso, no custom test runner is required – run your clojure.test tests as usual.\n\nCheck the [ideas driving eg](doc/ideas.md).\n\n## Installation\n\n**Leiningen/Boot**\n\n```clj\n[eg \"0.5.6-alpha\"]\n```\n**Clojure CLI/deps.edn**\n```clj\neg {:mvn/version \"0.5.6-alpha\"}\n```\n\n## Usage\nLet's try *eg*! Start by creating a REPL session and then requiring `eg` and `ge`:\n```clj\n(require '[eg :refer [eg ge]])\n```\n`eg` stands for *e.g.* (short for example), and `ge` is just `eg` reversed.\n\nEach *eg* test tests one function using examples. You could think of it as a function's test definition:\n```clj\n(eg not  ; testing clojure.core/not\n  false  ; with input parameter `false`\n  true)  ; returning expected value `true`\n```\na `clojure.test` test named `not-test` was generated.\n\nIf a function under test takes multiple parameters, we need to wrap these with `[]`:\n```clj\n(eg * [3 2] 6)\n```\n\nWhen parameter is a vector, it needs to be wrapped with `[]` as well:\n```clj\n(eg set\n  [[1 2]] #{1 2}) ; cannot do: [1 2] #{1 2}\n```\n\nEach *eg* test can contain an arbitrary number of examples:\n```clj\n(eg *\n  [3]   3\n  [3 2] 6)\n```\n\nThere are times when we prefer to have expected values\non the left, and input parameters on the right.\nFor that we use `ge`, a useful mnemonic for the inverted flow of the test example:\n```clj\n(ge + 10 [3 7])\n```\n\nExpected values can be checked against a predicate or a spec:\n```clj\n(require '[clojure.spec.alpha :as spec])\n\n(spec/def ::int integer?)\n\n(eg dec\n  [4] integer?\n  [5] ::int)\n```\n\n`=\u003e` or `\u003c=` operators between input parameters and expected value can be used to improve readability, or\noverride the default example direction of `eg` or `ge`.\n```clj\n(eg hash-map\n  [:d 1] {:d 1}\n  [:a 1 :b 2 :c 3 :d 4] =\u003e {:a 1 :b 2 :c 3 :d 4})\n```\n\n`=\u003e` or `\u003c=` could be read as *is*. `{:a 1 :b 2 :c 3 :d 4}` *is* a `map?`:\n```clj\n(eg hash-map\n  map? \u003c= {:a 1}\n  {:a 1} coll?) ; when no operator is used, 'is' operator semantics are assumed\n```\n\n`ex` makes it possible to test the result from evaluating an arbitrary s-**ex**pression. Typical scenarios include testing the result of calling a macro (`eg`, and `ge` only support function testing), or decomposing the assertion of different properties or values from calling a form:\n```clj\n; let must be used outside of 'ex' when surrounding examples\n(let [test-eg-ret (ex (inc 0) =\u003e 1)\n      f-len (count \"eg-test-\")]\n  ; using arrows/equal is compulsory\n(let [test-eg-ret (ex (inc 0) 1)\n      f-len (count \"eg-test-\")]\n  (ex var? \u003c= test-eg-ret)\n  (ex (-\u003e test-eg-ret meta :test) =\u003e boolean)\n  (ex (-\u003e test-eg-ret meta :test) =\u003e fn?)\n  (ex (-\u003e test-eg-ret meta :name name (subs f-len)) =\u003e not-empty))\n  ;=\u003e eg-test-\u003crand-id\u003e\n\n  (ex (true? false) =\u003e false) ;=\u003e eg-test-\u003crand-id\u003e\n\n; when '=\u003e' is omitted, \u003cexpected | checker\u003e and \u003cactual\u003e placeholders are\n; reversed. This syntax is ideal for multiline tests\n\n(ex \"Long string to examplify 'ex' syntax that is ideal for multiline tests\"\n    (identity \"Long string to examplify 'ex' syntax that is ideal for multiline tests\"))\n;=\u003e eg-test-\u003crand-id\u003e\n\n(ex string?\n    \"Long string to examplify 'ex' syntax that is ideal for multiline tests\")\n;=\u003e eg-test-\u003crand-id\u003e\n\n; tests truthy expression\n(ex \"bar\")\n;=\u003e eg-test-\u003crand-id\u003e\n\n; checks expression against a spec\n; recommended to do (eg ::fixtures/string \"foo\") instead,\n; but implemented for consistency\n(ex \"foo\" =\u003e ::fixtures/string)\n;=\u003e eg-test-\u003crand-id\u003e\n```\n\nIf we want to check if the expected value is a function or a spec, the operator `=` is used:\n```clj\n(defn foo [] inc)\n\n(eg foo [] = inc)\n; or\n(ge foo [] = 2)\n\n(ex (foo 2) = inc)\n\n(ex inc = (foo))\n\n(ex (foo) = inc)\n\n(defn bar [] ::inc)\n\n(eg bar [] ::inc)\n```\n\nThere are times when we just want to test a certain input parameter value, but fill the\nremainder input parameters nevertheless. *eg* provides a *don't care* placeholder `_`,\nfor these cases:\n```clj\n(eg vector\n  [1 2 3 4] [1 2 3 4]\n  [5 6 _ 8] vector?\n  [4 _ 5]   vector?)\n```\n\nWe can arbitrarily name a *don't care* parameter by prefixing its name with `$`. A *named don't care* can also be bound with parts on the expected result. *Don't care* parameters are generated from parameters on the same argument position:\n```clj\n(eg assoc-in\n  [{} [:a :b] {:eggs \"boiled\"}] =\u003e {:a {:b {:eggs \"boiled\"}}}\n  [_ $spam _] =\u003e map?\n  [_ _ $eggs] =\u003e {:a {:b $eggs}})\n```\nWhen writing examples, *don't cares* enable us to spend less time writting fillers, and the reader is able to better understand the focus\nof the example.\n\nExpecting arbitrarily nested JavaScript objects or arrays works out of the box:\n```clj\n(eg identity\n  #js {:a [1]} =\u003e #js {:a [1]}\n  #js {:a [1]} =\u003e (clj-\u003ejs {:a [1]}))\n\n(ex (identity #js {:a [1]}) =\u003e #js {:a [1]}))\n```\n\nCheck if your specs are on the right track using examples.\n`eg` validates examples against a spec defined with a qualified keyword:\n```clj\n(require '[clojure.spec.alpha :as spec])\n\n(spec/def ::string (spec/nilable string?))\n(spec/def ::pos-int pos-int?)\n\n(eg ::string nil \"foo\") ; `^:focus` cannot be used here at the moment\n;=\u003e \u003ccurrent-ns\u003e-:string-test\n\n(ge ::pos-int\n  1\n  ; test invalid examples, i.e., near a spec's boundary, using `!`\n  ! 0\n  (! -1 -2 -3)) ; equivalent to: ! -1 ! -2 ! -3\n```\n\nQuite often, writing tests becomes an afterthought, because creating test boilerblate like a new test namespace, requiring test forms and functions under test is too much of a hassle, while being immersed in writing code. It would make sense to have test forms globally available that we use almost as often as `defn`. Introducing `set-eg!` – call it at the development entrypoint of your program:\n```clj\n(require '[eg :refer [set-eg!]])\n```\nTo use `eg`, `ge`, and `ex` in any namespace without additional requires, run:\n```clj\n(set-eg!)\n;=\u003e :reloading ()\n```\nTo use `eg`, and `ge` in any namespace without additional requires, run:\n```clj\n(set-eg! 'eg 'ge)\n```\n\nPS - This functionality is only supported in Clojure.\n\nIt's possible to run only selected tests by using metadata `^:focus` on `eg` or `ge`:\n```clj\n(eg ^:focus false? [false] true)\n```\nThere are some caveats to consider when using `^:focus` **with ClojureScript**:\n  1. The tests report counts towards non focused tests, although examples under such tests are not executed.\n  2. Assertions for tests defined directly with `clojure.test/deftest` will still be executed, despite the presence of focused `eg`, or `ge` tests. \n\nSkip running certain tests or examples using vanilla Clojure(Script) code:\n```clj\n#_(eg count '(9 8 7)\n            3)\n\n(eg count\n ; '(9 8 7) 3\n '(9 8 7 6) 4)\n```\n\nBetween `eg`, and `ge`, choose the form that is most convenient for your combination of function examples and use it **only once** for testing a function. For example, **don't do this**:\n```clj\n(ge inc [1] 2)\n(ge inc [0] 1)\n```\n**or this:**\n```clj\n(eg inc [1] 2)\n(ge inc [0] 1)\n```\n\nWhen building with ClojureScript JVM, remove `WARNING: eg is a single segment namespace` warning with the compiler option `single-segment-namespace`:\n```clj\n{:cljsbuild\n  {:builds\n    [{:compiler\n       {:warnings\n         {:single-segment-namespace false}}}]}}\n```\n\n## Run your tests\nFinally, run your tests as you normally would with `clojure.test`.\n\n### Clojure tests in the REPL\n```clj\n(clojure.test/run-all-tests)\n; or\n(clojure.test/run-tests some.ns)\n```\n\n### Clojure tests in the terminal\n```\n\u003e lein test\n```\n\n### ClojureScript tests in the REPL\nAlso see below *Enable line information for ClojureScript (JVM) tests on Node.js*.\n```clj\n(cljs.test/run-all-tests)\n; or\n(cljs.test/run-tests some.ns)\n```\n\n### Enable line information for ClojureScript (JVM) tests on Node.js\nGet accurate test line information on reports.\n\nFirst install source maps support node package:\n```\nnpm install source-map-support\n```\nEnable source maps on your `project.clj` test build config:\n```clj\n{:cljsbuild\n  {:builds\n    {...\n      {:compiler {:source-map true}}}}}\n```\n\n# Run eg's own tests\n[Run eg's own tests](doc/egs-own-tests.md) expected to pass, fail, targeting Clojure, ClojureScript JVM, and ClojureScript JS.\n\n## Test libraries which work great with eg\n  * [eftest](https://github.com/weavejester/eftest) – Eftest is a fast and pretty Clojure test runner.\n  * [humane-test-output](https://github.com/pjstadig/humane-test-output) – Humane test output for clojure.test\n\n## Background readings\n  * https://seespotcode.net/2018/01/13/portable-clojure-test-assert-expr/\n  * http://blog.nberger.com.ar/blog/2015/09/18/more-portable-complex-macro-musing\n  * https://github.com/clojure/clojure/blob/master/src/clj/clojure/test.clj\n  * https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/test.cljs\n  * https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/test.cljc\n\n## [License](LICENSE.md)\nCopyright (c) 2020 Carlos da Cunha Fontes\n\nThe MIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fccfontes%2Feg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fccfontes%2Feg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fccfontes%2Feg/lists"}