{"id":19239134,"url":"https://github.com/lambdahands/opticlj","last_synced_at":"2025-08-19T22:32:02.299Z","repository":{"id":62433904,"uuid":"104436846","full_name":"lambdahands/opticlj","owner":"lambdahands","description":"A Clojure(Script) expectation/snapshot testing library, inspired by cram, ppx_expect, and jest","archived":false,"fork":false,"pushed_at":"2024-06-17T19:35:09.000Z","size":29,"stargazers_count":26,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-12T03:03:17.777Z","etag":null,"topics":["clojure","repl","snapshot-testing","test-strategy","testing"],"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/lambdahands.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":"2017-09-22T05:54:20.000Z","updated_at":"2024-09-04T16:18:22.000Z","dependencies_parsed_at":"2024-11-09T16:40:31.507Z","dependency_job_id":null,"html_url":"https://github.com/lambdahands/opticlj","commit_stats":{"total_commits":24,"total_committers":3,"mean_commits":8.0,"dds":"0.45833333333333337","last_synced_commit":"0792ffeaeeeed1c5c709dd42d540ad8037d46c3e"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdahands%2Fopticlj","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdahands%2Fopticlj/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdahands%2Fopticlj/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdahands%2Fopticlj/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lambdahands","download_url":"https://codeload.github.com/lambdahands/opticlj/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230066709,"owners_count":18167542,"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":["clojure","repl","snapshot-testing","test-strategy","testing"],"created_at":"2024-11-09T16:37:30.644Z","updated_at":"2024-12-19T04:05:51.320Z","avatar_url":"https://github.com/lambdahands.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# opticlj\n\nopticlj is a Clojure(Script) expectation testing (also known as snapshot testing)\nlibrary.\n\n## Rationale\n\nExpectations, or snapshots, is an automated testing strategy that captures the\noutput of a program as a reference to its correctness. In contrast to unit\ntesting, snapshots don't require the programmer to _specify_ the correct output\nof their program but instead to _verify_ the output.\n\n`opticlj` let's you define these snapshots and automatically generate the\noutputs into files. If you change the implementation of your program, the output\nfiles may be checked against the new behavior for differences.\n\nI was inspired by this testing strategy because it navigates elegantly between\nREPL driven development and testing. Unit testing is often cumbersome, but I've\nfound it to be even more so while writing Clojure code: I often _verify_ the\ncorrectness of my functions by simply evaluating them, but that output doesn't\npersist outside of my own machine.\n\nSnapshot testing may be a way for Clojure developers to cast a wide net over\nthe correctness of their programs while staying close to the REPL.\n\n### Use Cases\n\nSnapshot testing is often a great substitute to unit testing, but it in no way\nhas the power to verify programs as thoroughly as property-based testing.\nSnapshot tests are best used for _pure functions_, and aren't recommended in\ncases where correctness must be _\"proven\"_ (big air quotes).\n\n**Inspirations**\n\n- [Snapshot Testing in Swift](http://www.stephencelis.com/2017/09/snapshot-testing-in-swift)\n- [Testing with expectations](https://blog.janestreet.com/testing-with-expectations/)\n- [Mercurial: adding 'unified' tests](https://www.selenic.com/blog/?p=663)\n- [Jest: Snapshot Testing](https://facebook.github.io/jest/docs/en/snapshot-testing.html)\n\n## Installation\n\n```\n[opticlj \"1.0.0-alpha10\"]\n```\n\n[See on Clojars](https://clojars.org/opticlj)\n\n\n**Disclaimer**\n\nopticlj is alpha software, and its API is likely subject to change.\n\n## Usage\n\nThe below example is a way to get started with opticlj in Clojure.\n\nRequire the `opticlj.core` namespace to get started:\n\n```clj\n(ns my-project.core-test\n  (:require [opticlj.core :as optic :refer [defoptic]]))\n```\n\nLet's define a function to test:\n\n```clj\n(defn add [x y]\n  (+ x y))\n```\n\nDefine an `optic` like so:\n\n```clj\n(defoptic ::one-plus-one (add 1 1))\n```\n\nThis does two things:\n\n- Defines \"runner\" function that can be accessed with `opticlj.core/run`\n- Writes an output file in `test/__optic__/my_project/core_test/one_plus_one.clj`\n\nHere's what `one_plus_one.clj` looks like:\n\n```clj\n(in-ns 'my-project.core-test)\n\n(add 1 1)\n\n2\n```\n\nThe `in-ns` expression allows us to evaluate this file, which is especially\nuseful if your editor integrates with the REPL.\n\nNext, if we change the implementation of `add` and re-run the optic, we get\noutput confirming the snapshot was checked:\n\n```clj\n(defn add [x y]\n  (+ x y 2))\n\n(run ::one-plus-one)\n\n; outputs\n{:file \"test/__optic__/my_project/core_test/one_plus_one.clj\"\n :err-file \"test/__optic__/my_project/core_test/one_plus_one.err.clj\"\n :passing? false\n :diff {:string \"\u003ctruncated\u003e\"}\n :form (add 1 1)\n :result 4\n :kw :my-project.core-test/one-plus-one}\n```\n\nA new file was created: `test/__optic__/my_project/core_test/one_plus_one.err.clj`\n\n```clj\n(in-ns 'my-project.core-test)\n\n(add 1 1)\n\n4\n```\n\nAlso, note how the `:passing?` key is `false`. We can view our error diff by\ncalling `optic/errors`:\n\n```clj\n(optic/errors)\n; prints\n--- test/__optic__/my_project/core_test/one_plus_one.clj   2017-09-22 16:03:38.000000000 -0500\n+++ -   2017-09-22 16:04:38.000000000 -0500\n@@ -2,4 +2,4 @@\n\n (add 1 1)\n\n-2\n+4\n```\n\nWhat we get back is essentially the output of running:\n\n```\necho \"...my new output...\" | diff -u \u003coutput-file\u003e -\n```\n\nLet's say we wanted to change the rules of our universe and make the addition\nof one and one equal to four. We can `adjust!` our optic to accept these new rules:\n\n```clj\n(optic/adjust! ::one-plus-one)\n\n; outputs\n{:adjusted {:file \"test/__optic__/my_project/core_test/one_plus_one.clj\"\n            :passing? true\n            :diff nil\n            :err-file nil\n            :form (add 1 1)\n            :result 4\n            :kw :my-project.core-test/one-plus-one}}\n```\n\nNow when we check for errors, we see we have resolved our new form of arithmetic:\n\n```clj\n(optic/errors)\n\n; outputs\nnil\n```\n\n## ClojureScript\n\nopticlj supports ClojureScript with a few caveats, namely that in order to run\nClojureScript tests, you must output your test code using `:target :nodejs` in\nyour compiler options. See the [test/opticlj/cljs](test/opticlj/cljs/) directory\nfor an example of using opticlj with the [doo](https://github.com/bensu/doo)\ntest runner.\n\nA convenience function, `opticlj.core/ok?`, exists to wrap opticlj's tests\nin a `cljs.test/deftest` expression. For example:\n\n```clj\n(ns my-project.cljs.core-test\n  (:require [cljs.test :as test :refer-macros [deftest]]\n            [opticlj.core :as optic :refer-macros [defoptic]]))\n\n(defoptic ::two-plus-two (+ 2 2))\n\n(deftest optics\n  (test/is (optic/passing? (optic/review!))))\n```\n\n## Todo\n\n- [x] Warn if optics is undefined in the program yet exists in a file\n- [x] Add a `clean!` method to remove unused optics\n- [x] Use `defoptic` on `defoptic` _(Inception noise)_\n- [ ] Complete API documentation\n- [ ] Reimplement core API with stateless methods\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flambdahands%2Fopticlj","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flambdahands%2Fopticlj","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flambdahands%2Fopticlj/lists"}