{"id":13829753,"url":"https://github.com/Day8/re-frame-test","last_synced_at":"2025-07-09T10:31:35.840Z","repository":{"id":41258147,"uuid":"64102309","full_name":"day8/re-frame-test","owner":"day8","description":"Cross platform (cljs and clj) utilities for testing re-frame applications","archived":false,"fork":false,"pushed_at":"2021-11-04T23:04:46.000Z","size":195,"stargazers_count":109,"open_issues_count":4,"forks_count":12,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-11-13T00:11:31.306Z","etag":null,"topics":["re-frame","test"],"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/day8.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"mike-thompson-day8"}},"created_at":"2016-07-25T03:56:26.000Z","updated_at":"2024-01-28T02:56:04.000Z","dependencies_parsed_at":"2022-09-02T23:02:01.246Z","dependency_job_id":null,"html_url":"https://github.com/day8/re-frame-test","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/day8%2Fre-frame-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/day8%2Fre-frame-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/day8%2Fre-frame-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/day8%2Fre-frame-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/day8","download_url":"https://codeload.github.com/day8/re-frame-test/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225533019,"owners_count":17484179,"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":["re-frame","test"],"created_at":"2024-08-04T10:00:44.429Z","updated_at":"2024-11-20T10:31:10.662Z","avatar_url":"https://github.com/day8.png","language":"Clojure","funding_links":["https://github.com/sponsors/mike-thompson-day8"],"categories":["Tools"],"sub_categories":["Testing"],"readme":"\u003c!--\n[![CI](https://github.com/day8/re-frame-test/workflows/ci/badge.svg)](https://github.com/day8/re-frame-test/actions?workflow=ci)\n[![CD](https://github.com/day8/re-frame-test/workflows/cd/badge.svg)](https://github.com/day8/re-frame-test/actions?workflow=cd)\n[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/day8/re-frame-test?style=flat)](https://github.com/day8/re-frame-test/tags)\n[![GitHub pull requests](https://img.shields.io/github/issues-pr/day8/re-frame-test)](https://github.com/day8/re-frame-test/pulls)\n--\u003e\n[![Clojars Project](https://img.shields.io/clojars/v/day8.re-frame/test?style=for-the-badge\u0026logo=clojure\u0026logoColor=fff)](https://clojars.org/day8.re-frame/test)\n[![GitHub issues](https://img.shields.io/github/issues-raw/day8/re-frame-test?style=for-the-badge\u0026logo=github)](https://github.com/day8/re-frame-test/issues)\n[![License](https://img.shields.io/github/license/day8/re-frame-test?style=for-the-badge)](LICENSE)\n\n# re-frame-test\n\nThis library provides utilities \nfor testing [re-frame applications](https://github.com/day8/re-frame).\n\nThese utilities:\n  - allow you to use `subscribe` in your tests\n  - allow you to use `dispatch` in your tests\n  - allow you to run tests on both the JVM and JS platforms\n  - allow you to create \"end to end\" integration tests, involving backend servers\n\nFor context, please be sure to read the \n[basic testing tutorial](https://github.com/day8/re-frame/blob/master/docs/Testing.md) \nin the main re-frame docs before going any further.\n\nThis library primarily supports the testing of Event Handlers, but Subscription Handlers\nget to come along for the ride.\n\n## Quick Start Guide\n\n### Step 1. Add Dependency\n\nAdd the following project dependency: \u003cbr\u003e\n[![Clojars Project](https://img.shields.io/clojars/v/day8.re-frame/test.svg)](https://clojars.org/day8.re-frame/test)\n\nRequires re-frame \u003e= \"1.1.1\"\n\n### Step 2. Registration And Use\n\nIn the namespace where you register your tests, perhaps called `tests.cljs`, you have 2 things to do.\n\n**First**, add this require to the `ns`:\n```clj\n(ns app.tests\n  (:require\n    ...\n    [day8.re-frame.test :as rf-test]   ;; \u003c-- add this\n    ...))\n```\n\n**Second**, Define or Modify some tests. \n```Clojure\n(deftest init\n  (rf-test/run-test-sync               ;; \u003c-- add this \n    ;; with the above macro this becomes a dispatch-sync \n    ;; and app-db is isolated between tests\n    (rf/dispatch [:initialise-db])   \n    ;; Define subscriptions to the app state\n    (let [showing         (rf/subscribe [:showing])] \n      ;;Assert the initial state\n      (is (= :all @showing)))))\n```\n\n## How It Works\n\n`re-frame-test` provides two macros which dovetail with `cljs.test`.\n \n### run-test-sync\n\nExecute `body` as a test, where each `dispatch` call is executed\nsynchronously (via `dispatch-sync`), and any subsequent dispatches which are\ncaused by that dispatch are also fully handled/executed prior to control flow\nreturning to your test.\n\nThink of it as though every `dispatch` in your app had been magically\nturned into `dispatch-sync`, and re-frame had lifted the restriction that says\nyou can't call `dispatch-sync` from within an event handler.\n\nThis macro is applicable for most events that do not run async behaviour within the \nevent.\n\nFrom the todomvc example:\n\n```Clojure\n(defn test-fixtures\n  []\n  ;; change this coeffect to make tests start with nothing\n  (rf/reg-cofx\n    :local-store-todos\n    (fn [cofx _]\n      \"Read in todos from localstore, and process into a map we can merge into app-db.\"\n      (assoc cofx :local-store-todos\n                  (sorted-map)))))\n```\n\nDefine some test-fixtures. In this case we have to ignore the localstore\nin the tests.\n\n```Clojure\n(deftest basic--sync\n  (rf-test/run-test-sync\n    (test-fixtures)\n    (rf/dispatch [:initialise-db])\n```\n\nUse the `run-test-sync` macro to construct the tests and initialise the app state.\nNote that, the `dispatch` will be handled before the following code is executed, \neffectively turning it into a `dispatch-sync`. Also any changes to the database\nand registrations will be rolled back at the termination of the test, therefore \nour fixtures are run within the `run-test-sync` macro.\n\n```Clojure\n        ;; Define subscriptions to the app state\n        (let [showing         (rf/subscribe [:showing])\n              sorted-todos    (rf/subscribe [:sorted-todos])\n              todos           (rf/subscribe [:todos])\n              visible-todos   (rf/subscribe [:visible-todos])\n              all-complete?   (rf/subscribe [:all-complete?])\n              completed-count (rf/subscribe [:completed-count])\n              footer-counts   (rf/subscribe [:footer-counts])] \n                 \n          ;;Assert the initial state\n          (is (= :all @showing))\n          (is (empty? @sorted-todos))\n          (is (empty? @todos))\n          (is (empty? @visible-todos))\n          (is (= false (boolean @all-complete?)))\n          (is (= 0 @completed-count))\n          (is (= [0 0] @footer-counts)) \n             \n          ;;Dispatch the event that you want to test, remember that `re-frame-test` \n          ;;will process this event immediately.\n          (rf/dispatch [:add-todo \"write first test\"])\n              \n          ;;Test that the dispatch has mutated the state in the way that we expect.\n          (is (= 1 (count @todos) (count @visible-todos) (count @sorted-todos)))\n          (is (= 0 @completed-count))\n          (is (= [1 0] @footer-counts))\n          (is (= {:id 1, :title \"write first test\", :done false}\n                 (first @todos)))\n```\n    \n### run-test-async\n\nThis macro is applicable for events that do run some async behaviour \n(usually outside or re-frame, e.g. an http request) within the event.\n\nRun `body` as an async re-frame test. The async nature means you'll need to\nuse `wait-for` any time you want to make any assertions that should be true\n*after* an event has been handled.  It's assumed that there will be at least\none `wait-for` in the body of your test (otherwise you don't need this macro\nat all).\n\nNote: unlike regular ClojureScript `cljs.test/async` tests, `wait-for` takes\ncare of calling `(done)` for you: you don't need to do anything specific to\nhandle the fact that your test is asynchronous, other than make sure that all\nyour assertions happen with `wait-for` blocks.\n\nFrom the todomvc example:\n\n```Clojure\n(deftest basic--async\n  (rf-test/run-test-async\n    (test-fixtures)\n    (rf/dispatch-sync [:initialise-db])\n```\n\nUse the `run-test-async` macro to construct the tests and initialise the app state\nnote that the `dispatch-sync` must be used as this macro does not run the dispatch\nimmediately like `run-test-sync`. Also any changes to the database\nand registrations will be rolled back at the termination of the test, therefore\nour fixtures are run within the `run-test-async` macro.\n\n```Clojure    \n    ;;Define subscriptions to the app state\n    (let [showing         (rf/subscribe [:showing])\n          sorted-todos    (rf/subscribe [:sorted-todos])\n          todos           (rf/subscribe [:todos])\n          visible-todos   (rf/subscribe [:visible-todos])\n          all-complete?   (rf/subscribe [:all-complete?])\n          completed-count (rf/subscribe [:completed-count])\n          footer-counts   (rf/subscribe [:footer-counts])]\n          \n      ;;Assert the initial state\n      (is (= :all @showing))\n      (is (empty? @sorted-todos))\n      (is (empty? @todos))\n      (is (empty? @visible-todos))\n      (is (= 0 @completed-count))\n                    \n      ;;Dispatch the event that you want to test, remember \n      ;;that re-frame will not process this event immediately, \n      ;;and need to use the `wait-for` macro to continue the tests.\n      (rf/dispatch [:add-todo \"write first test\"])\n          \n          \n      ;;Wait for the `:add-todo` event to be dispatched \n      ;;(note, in the use of this macro we would typically wait for \n      ;;an event that had been triggered by the successful return of \n      ;;the async event).        \n      (rf-test/wait-for [:add-todo-finished]\n          \n        ;;Test that the dispatch has mutated the state in the way \n        ;;that we expect.    \n        (is (= [{:id 1, :title \"write first test\", :done false}] @todos))\n```\n\nHere we have assumed that the `:add-todo` event will make some sort of async \ncall which will in turn generate an `add-todo-finished` event when it has finished.\nThis is not actually the case in the example code.\n\n## Running the CLJS tests with Karma\n\nYou will need `npm`, with:\n\n```console\n$ npm install -g karma karma-cli karma-cljs-test karma-junit-reporter karma-chrome-launcher\n```\n\nAnd you will need Chrome.\n\n\n## License\n\nCopyright (c) 2016 Mike Thompson\n\nDistributed under the The MIT License (MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDay8%2Fre-frame-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDay8%2Fre-frame-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDay8%2Fre-frame-test/lists"}