{"id":21436723,"url":"https://github.com/amperity/greenlight","last_synced_at":"2025-04-12T11:18:58.698Z","repository":{"id":33275582,"uuid":"124963146","full_name":"amperity/greenlight","owner":"amperity","description":"Clojure integration testing framework","archived":false,"fork":false,"pushed_at":"2024-06-24T19:35:11.000Z","size":302,"stargazers_count":137,"open_issues_count":13,"forks_count":11,"subscribers_count":57,"default_branch":"main","last_synced_at":"2025-04-12T11:18:50.774Z","etag":null,"topics":["clojure","integration-testing","test-framework"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/amperity.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-12T23:15:28.000Z","updated_at":"2025-03-24T07:54:52.000Z","dependencies_parsed_at":"2024-06-24T19:30:18.990Z","dependency_job_id":"de2272bb-1238-4cd8-9156-74df4b4cceb6","html_url":"https://github.com/amperity/greenlight","commit_stats":{"total_commits":169,"total_committers":15,"mean_commits":"11.266666666666667","dds":0.4970414201183432,"last_synced_commit":"783e6fe747926b3f2edeb8d7b3d2f3cf08aa3a09"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amperity%2Fgreenlight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amperity%2Fgreenlight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amperity%2Fgreenlight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amperity%2Fgreenlight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amperity","download_url":"https://codeload.github.com/amperity/greenlight/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248557847,"owners_count":21124168,"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","integration-testing","test-framework"],"created_at":"2024-11-23T00:14:12.396Z","updated_at":"2025-04-12T11:18:58.676Z","avatar_url":"https://github.com/amperity.png","language":"Clojure","readme":"Greenlight\n==========\n\n[![CircleCI](https://circleci.com/gh/amperity/greenlight.svg?style=shield\u0026circle-token=6db00254bc95e32ec743f6e7e7e812c05f436f88)](https://circleci.com/gh/amperity/greenlight)\n\nThis library provides an _integration testing_ framework for Clojure. Running\na suite of tests against your systems gives you the confidence to _greenlight_\nthem for promotion to production. The primary goals of this framework are:\n\n- Steps should be _composable_ to drive down repetition in similar tests.\n- Tests should support parallelization out-of-the-box.\n- Results should be _actionable_ and easy to understand.\n\n\n## Installation\n\nLibrary releases are published on Clojars. To use the latest version with\nLeiningen, add the following to your project dependencies:\n\n[![Clojars Project](http://clojars.org/amperity/greenlight/latest-version.svg)](http://clojars.org/amperity/greenlight)\n\n\n## Usage\n\nA quick overview of greenlight usage:\n\n```clojure\n(require\n  '[greenlight.test :as test :refer [deftest]]\n  '[greenlight.step :as step :refer [defstep]]\n  '[greenlight.runner :as runner]\n  '[clojure.test :refer [is]])\n\n;; Greenlight tests are built from a collection of steps\n\n(defstep math-test\n  \"A simple test step\"\n  :title \"Simple Math Test\"\n  :test (fn [_] (is (= 3 (+ 1 2)))))\n=\u003e #'user/math-test\n\n(deftest simple-test\n  \"A simple test of addition\"\n  (math-test))\n=\u003e #'user/simple-test\n\n;; Tests and steps are just data\n\n(simple-test)\n=\u003e {:greenlight.test/description \"A simple test of addition\",\n    :greenlight.test/line 14,\n    :greenlight.test/ns user,\n    :greenlight.test/steps [{:greenlight.step/inputs {},\n                             :greenlight.step/name math-test,\n                             :greenlight.step/test #\u003cFn@7bb15aaa user/math_test[fn]\u003e,\n                             :greenlight.step/title \"Simple Math Test\"}],\n    :greenlight.test/title \"simple-test\"}\n\n\n;; Tests can be ran individually\n\n(test/run-test! {} (simple-test))\n=\u003e {:greenlight.test/context {},\n    :greenlight.test/description \"A simple test of addition\",\n    :greenlight.test/ended-at #\u003cjava.time.Instant@55e7469c 2018-07-01T17:03:29.811Z\u003e,\n    :greenlight.test/line 14,\n    :greenlight.test/ns user,\n    :greenlight.test/outcome :pass,\n    :greenlight.test/started-at #\u003cjava.time.Instant@224450d6 2018-07-01T17:03:29.808Z\u003e,\n    :greenlight.test/steps [{:greenlight.step/cleanup [],\n                             :greenlight.step/elapsed 0.002573744,\n                             :greenlight.step/inputs {},\n                             :greenlight.step/message \"1 assertions (1 pass)\",\n                             :greenlight.step/name math-test,\n                             :greenlight.step/outcome :pass,\n                             :greenlight.step/reports [{:actual (3),\n                                                        :expected 3,\n                                                        :message nil,\n                                                        :type :pass}],\n                             :greenlight.step/test #\u003cFn@2be25eaa user/math_test[fn]\u003e,\n                             :greenlight.step/title \"Simple Math Test\"}],\n    :greenlight.test/title \"simple-test\"}\n\n;; Or as part of a suite with configurable reporters\n\n(runner/run-tests! (constantly {}) [(simple-test)] {})\n\n; Starting test system...\n; Running 1 tests...\n;\n;  * Testing simple-test\n;  | user:124\n;  | A simple test of addition\n;  |\n;  +-\u003e\u003e Simple Math Test\n;  | 1 assertions (1 pass)\n;  | [PASS] (0.000 seconds)\n;  |\n;  |\n;  * PASS (0.001 seconds)\n;\n;\n; Ran 1 tests containing 1 steps with 1 assertions:\n; * 1 pass\n\n=\u003e true\n```\n\n\n### Test System\n\nTests are executed with a [system](https://github.com/stuartsierra/component)\nof components. The component lifecycle is managed by the test runner: components\nare started on test startup and then stopped on test completion.\n\n```clojure\n(require '[com.stuartsierra.component :as component])\n\n(defrecord TestComponent\n  []\n\n  component/Lifecycle\n  (start [this]\n    (println \"Starting test component\")\n    this)\n\n  (stop [this]\n    (println \"Stopping test component\")\n    this))\n\n(runner/run-tests!\n  (constantly (component/system-map ::component (-\u003eTestComponent)))\n  [(simple-test)]\n  {})\n\n; Starting test system...\n; Starting test component\n; Running 1 tests...\n;\n;  * Testing simple-test\n;  | user:690\n;  | A simple test of addition\n;  |\n;  +-\u003e\u003e Simple Math Test\n;  | 1 assertions (1 pass)\n;  | [PASS] (0.000 seconds)\n;  |\n;  |\n;  * PASS (0.001 seconds)\n;\n;\n; Ran 1 tests containing 1 steps with 1 assertions:\n; * 1 pass\n;\n; Stopping test component\n=\u003e true\n```\n\nIf you wish to hook into another system library such as [integrant](https://github.com/weavejester/integrant),\nuse the `ManagedSystem` protocol provider in the runner namespace, which supports extension via metadata:\n\n```clojure\n(extend-protocol runner/ManagedSystem\n  java.util.Map\n  (start-system [this] (integrant/init this))\n  (stop-system [this] (integrant/halt! this)))\n\n(runner/run-tests! (constantly {:some-system-map :with-components})\n                   tests {})\n\n;;;Alternatively:\n\n(runner/run-tests! (constantly\n                     (with-meta {:some-system-map :with-components}\n                       {`runner/start-system (fn [this] (println \"Starting test system...\") this)\n                        `runner/stop-system (fn [this] (println \"Stopping test system...\") nil)}))\n                   tests {})\n```\n\n\n### Step Inputs and Outputs\n\nTest steps support parameterization through their inputs. Inputs can be statically\nconfigured, pull components from the test system, or pull values built up from\nprevious steps in the test context.\n\nSteps can additionally have outputs. These outputs are registered in the test\ncontext and passed to subsequent steps. Outputs can be registered under a key,\na collection of keys, or as a function of the previous context.\n\n```clojure\n(defstep step-with-output-keyword\n  \"A step demonstrating a keyword output\"\n  :title \"Keyword output\"\n  :test (constantly 1)\n  :output :foo)\n\n(defstep step-with-collection-output\n  \"A step demonstrating a nested output\"\n  :title \"Nested output\"\n  :test (constantly 2)\n  :output [:a :b :c])\n\n(defstep step-with-inputs\n  \"A step demonstrating different input types\"\n  :title \"Step with inputs\"\n  :inputs {;; For defaulting values\n           :a 1\n           ;; Extracting from test system\n           :b (step/component ::component)\n           ;; Extracting from test context\n           :c (step/lookup :foo)\n           ;; Context lookups can be a collection\n           :d (step/lookup [:a :b :c])\n           ;; Or for ultimate flexibility: a function\n           :e (step/lookup (fn [ctx] (:bar ctx)))}\n  :test (fn [{:keys [a b c d e]}]\n          (is (every? some? [a b c d e]))))\n\n(deftest inputs-and-outputs\n  \"A test demonstrating inputs and outputs\"\n  (step-with-output-keyword)\n  (step-with-collection-output)\n  ;; Steps can also be defined inline\n  #::step{:title \"function output\"\n          :name 'step-with-function-output\n          :test (constantly 3)\n          :output (fn [ctx test-output]\n                    (assoc ctx :bar (* 2 test-output)))}\n  (step-with-inputs\n    ;; Override step inputs\n    {:a :override-default}\n    ;; Override step configuration\n    :output :new-output\n    :title \"New Title\"))\n\n(runner/run-tests!\n  (constantly (component/system-map ::component (-\u003eTestComponent)))\n  [(inputs-and-outputs)]\n  {})\n\n; Starting test system...\n; Starting test component\n; Running 1 tests...\n;\n;  * Testing inputs-and-outputs\n;  | user:36\n;  | A test demonstrating inputs and outputs\n;  |\n;  +-\u003e\u003e Keyword output\n;  | 0 assertions ()\n;  | [PASS] (0.000 seconds)\n;  |\n;  +-\u003e\u003e Nested output\n;  | 0 assertions ()\n;  | [PASS] (0.000 seconds)\n;  |\n;  +-\u003e\u003e Function output\n;  | 0 assertions ()\n;  | [PASS] (0.000 seconds)\n;  |\n;  +-\u003e\u003e New Title\n;  | 1 assertions (1 pass)\n;  | [PASS] (0.000 seconds)\n;  |\n;  |\n;  * PASS (0.003 seconds)\n;\n;\n; Ran 1 tests containing 4 steps with 1 assertions:\n; * 1 pass\n;\n; Stopping test component\n=\u003e true\n```\n\n\n### Step Cleanup\n\nOften, steps will create some resource that should be removed on test completion.\nThe `step/clean!` multimethod along with `step/register-cleanup!` allows for test\nsteps to register resource specific cleanups. These cleanups are performed in\nreverse order of registration.\n\n```clojure\n(defmethod step/clean! :foo/bar\n  [system resource-type k]\n  (printf \"Removing %s resource %s\" resource-type k)\n  ;; Actual cleanup using system component\n  )\n\n(defstep step-with-cleanup\n  \"A step demonstrating resource cleanup\"\n  :title \"Step with cleanup\"\n  :test (fn [_]\n          (step/register-cleanup! :foo/bar :my-key)))\n\n(deftest test-with-cleanup\n  \"A test demonstrating resource cleanup\"\n  (step-with-cleanup))\n\n(runner/run-tests!\n  (constantly {})\n  [(test-with-cleanup)]\n  {})\n\n; Starting test system...\n; Running 1 tests...\n;\n;  * Testing test-with-cleanup\n;  | user:13\n;  | A test demonstrating resource cleanup\n;  |\n;  +-\u003e\u003e Step with cleanup\n;  | 0 assertions ()\n;  | [PASS] (0.000 seconds)\n;  |\n;  +-\u003e\u003e Cleaning :foo/bar resource :my-key\n; Removing :foo/bar resource :my-key |\n;  * PASS (0.000 seconds)\n;\n; Ran 1 tests containing 1 steps with 0 assertions:\n; * 1 pass\n=\u003e true\n```\n\n\n### Test Discovery\n\nTests can be specified explictly when running tests, or by utilizing\n`runner/find-tests`, which retrieves tests based on a matcher. The matcher\ncan be either a keyword to match against test metadata or a regular expression\nto match test name.\n\n```clojure\n(deftest ^:quick test-1\n  \",,,\"\n  ,,,)\n\n(deftest test-2\n  \",,,\"\n  ,,,)\n\n(runner/find-tests :quick)\n=\u003e (#:greenlight.test{:description \",,,\", :title \"test-1\", :ns user, :line 1, :steps []})\n\n(runner/find-tests #\"test-2\")\n=\u003e (#:greenlight.test{:description \",,,\", :title \"test-2\", :ns user, :line 5, :steps []})\n```\n\n### Parallel Test Execution\n\nTests can be executed in parallel by providing a `--parallel` option\nwith a number of threads. Tests can be further grouped with `::test/group`\nmetadata to indicate that tests within the same group should run serially.\n\nIf a `::test/group` is not provided, a test is placed in its own group.\nGroups of tests are run in parallel.\n\n\n```clojure\n(deftest simple1\n  (math-test))\n\n(deftest simple2\n  {::test/group :sync}\n  (math-test))\n\n(deftest simple3\n  {::test/group :sync}\n  (math-test))\n\n;; Will run `simple1` and `simple2` concurrently, then\n;; `simple3` on completion of `simple2`.\n(runner/run-tests!\n  (constantly {})\n  [(simple1)\n   (simple2)\n   (simple3)]\n  {:parallel 2})\n```\n\n## Retrying steps\n\nIntegration tests can sometimes be slow, or reliant on less stable systems. When\nrunning such tests manually, it can be helpful to not have to rerun entire tests\nfrom the beginning. You can do this by passing `{:on-fail :prompt}` to the test\nrunner:\n\n```\n(runner/run-tests!\n  (constantly {})\n  [(simple1)\n   (simple2)\n   (simple3)]\n  {:on-fail :prompt})\n```\n\nWhen a step fails, it will now ask if you want to retry the step.\n\n## License\n\nLicensed under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file\nfor rights and restrictions.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famperity%2Fgreenlight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famperity%2Fgreenlight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famperity%2Fgreenlight/lists"}