{"id":28427154,"url":"https://github.com/pmatiello/mockfn","last_synced_at":"2025-06-24T20:31:14.358Z","repository":{"id":287420820,"uuid":"875052705","full_name":"pmatiello/mockfn","owner":"pmatiello","description":"A mocking library for Clojure.","archived":false,"fork":false,"pushed_at":"2025-04-29T18:14:15.000Z","size":146,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-05T12:09:24.708Z","etag":null,"topics":["clojure","mocking","testing"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pmatiello.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,"zenodo":null}},"created_at":"2024-10-19T01:40:14.000Z","updated_at":"2025-04-29T18:14:18.000Z","dependencies_parsed_at":"2025-04-11T16:57:20.187Z","dependency_job_id":"b9360d8a-b31a-4f6e-8dfc-cc879165f11f","html_url":"https://github.com/pmatiello/mockfn","commit_stats":null,"previous_names":["pmatiello/mockfn"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/pmatiello/mockfn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmatiello%2Fmockfn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmatiello%2Fmockfn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmatiello%2Fmockfn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmatiello%2Fmockfn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pmatiello","download_url":"https://codeload.github.com/pmatiello/mockfn/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmatiello%2Fmockfn/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261751508,"owners_count":23204441,"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","mocking","testing"],"created_at":"2025-06-05T12:09:26.869Z","updated_at":"2025-06-24T20:31:14.350Z","avatar_url":"https://github.com/pmatiello.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# me.pmatiello/mockfn\n\nThis is a library for mockist test-driven-development in Clojure. It is meant to\nbe used alongside a regular testing framework such as `clojure.test`.\n\n[![Clojars Project](https://img.shields.io/clojars/v/me.pmatiello/mockfn.svg)](https://clojars.org/me.pmatiello/mockfn)\n[![Documentation](https://cljdoc.org/badge/me.pmatiello/mockfn)](https://cljdoc.org/d/me.pmatiello/mockfn)\n\n## Usage\n\nInstructions for using this library.\n\n### Framework-agnostic usage\n\nIn order to use `mockfn`, it's enough to require it in a test namespace.\n\n```clj\n(:require [me.pmatiello.mockfn.plain :as mfn]\n          [me.pmatiello.mockfn.matchers :as mfn.m]\n          ...)\n```\n\nThis will bring `mockfn` features in scope for this namespace.\n\n#### Stubbing Function Calls\n\nThe `providing` macro replaces functions with mocks. These mocks return\npreconfigured values when called with the expected arguments.\n\n```clj\n(testing \"providing\"\n  (mfn/providing [(one-fn) :result]\n    (is (= :result (one-fn)))))\n```\n\nAs demonstrated below, a mock (`one-fn`) can be configured with different return\nvalues for different arguments.\n\n```clj\n(testing \"providing - one function, different arguments\"\n  (mfn/providing [(one-fn :argument-1) :result-1\n                  (one-fn :argument-2) :result-2]\n    (is (= :result-1 (one-fn :argument-1)))\n    (is (= :result-2 (one-fn :argument-2)))))\n```\n\nIt's also possible to configure multiple mocks, for multiple functions, at once.\n\n```clj\n(testing \"providing with more than one function\"\n  (mfn/providing [(one-fn :argument) :result-1\n                  (other-fn :argument) :result-2]\n    (is (= :result-1 (one-fn :argument)))\n    (is (= :result-2 (other-fn :argument))))))\n```\n\n#### Verifying Interactions\n\nThe `verifying` macro works similarly, but also defines an expectation for the\nnumber of times a call should be performed during the test. A test will fail if\nthis expectation is not met.\n\n```clj\n(testing \"verifying\"\n  (mfn/verifying [(one-fn :argument) :result (mfn.m/exactly 1)]\n    (is (= :result (one-fn :argument)))))\n```\n\nNotice that the expected number of calls is defined using a\n[matcher](#built-in-matchers).\n\n#### Argument Matchers\n\nMocks can be configured to return a specific value for a range of different\narguments through [matchers](#built-in-matchers).\n\n```clj\n(testing \"argument matchers\"\n  (mfn/providing [(one-fn (mfn.m/at-least 10) (mfn.m/at-most 20)) 15]\n    (is (= 15 (one-fn 12 18))))))\n```\n\n#### Mocking private functions\n\nPrivate functions can be mocked by referring to the `Var` associated with the\nsymbol of the mocked function.\n\n```clj\n(testing \"providing, private function\"\n  (mfn/providing [(#'pvt-fn) :result]\n    (is (= :result (#'pvt-fn)))))\n```\n\n```clj\n(testing \"verifying, private function\"\n  (mfn/verifying [(#'pvt-fn :argument) :result (mfn.m/exactly 1)]\n    (is (= :result (#'pvt-fn :argument)))))\n```\n\n#### Returning dynamic values\n\nThe `invoke` function allows mocks to dynamically invoke a function with the\nreceived arguments and return the output.\n\n```clj\n(testing \"returns dynamic values\"\n  (mfn/providing\n    [(one-fn (matchers/any)) (mfn/invoke identity)]\n    (is (= :x (#'one-fn :x)))\n    (is (= :y (#'one-fn :y)))))\n```\n\nThe same approach can be used to invoke the original implementation to test\nwhether a function has been invoked as expected without overriding its\nimplementation:\n\n```clj\n(testing \"invokes the original implementation\"\n  (mfn/verifying\n    [(one-fn (matchers/any)) (mfn/invoke one-fn) (matchers/exactly 2)]\n    (is (= :x (one-fn :x)))\n    (is (= :y (one-fn :y)))))\n```\n\n#### Throwing exceptions\n\nMocks can be configured to throw exceptions when invoked using the `raise`\nfunction. This can be used to simulate error scenarios in tests.\n\n```clj\n(testing \"throws an exception\"\n  (mfn/providing \n    [(one-fn) (mfn/raise (ex-info \"error!\" {}))]\n    (is (thrown-with-msg? ExceptionInfo #\"error!\" (one-fn)))))\n```\n\n### Syntax sugar for clojure.test\n\nSupport\nfor [clojure.test](https://clojure.github.io/clojure/clojure.test-api.html)\nis provided in the `mockfn.clj-test` namespace.\n\n```clj\n(:require [clojure.test :refer :all]\n          [me.pmatiello.mockfn.clj-test :as mfn]\n          [me.pmatiello.mockfn.matchers :as mfn.m]\n          ...)\n```\n\nThe `mockfn.clj-test/deftest` and `mockfn.clj-test/testing` macros replace\n`clojure.test/deftest` and `clojure.test/testing` and support a flatter (as in\nnot nested) mocking style using `mockfn.clj-test/providing` and\n`mockfn.clj-test/verifying`:\n\n```clj\n(mfn/deftest deftest-with-builtin-mocking\n  (is (= :one-fn (one-fn)))\n  (mfn/providing\n    (one-fn) :one-fn)\n\n  (mfn/testing \"testing with built-in-mocking\"\n    (is (= :one-fn (one-fn)))\n    (is (= :other-fn (other-fn)))\n    (mfn/verifying\n      (other-fn) :other-fn (mfn.m/exactly 1))))\n```\n\nNote that to leverage the built-in support for mocking in these macros, it's\nnecessary to use the `providing` and `verifying` versions provided in the\n`mockfn.clj-test` namespace.\n\n### Built-in Matchers\n\nThe following matchers are included in `mockfn`:\n\n| Matcher        | Description                                                            | Usage                   |\n|----------------|------------------------------------------------------------------------|-------------------------|\n| **Generic**    |                                                                        |                         |\n| `any`          | Always matches.                                                        | `(any)`                 |\n| `a`            | Matches if actual value is an instance of the expected type.           | `(a type)`              |\n| `exactly`      | Matches if actual value is equal to the expected value.                | `(exactly value)`       |\n| `empty`        | Matches if the actual value is empty.                                  | `(empty)`               |\n| `pred`         | Matches if the actual value satisfies the provided predicate function. | `(pred pred-fn)`        |\n| **Boolean**    |                                                                        |                         |\n| `truthy`       | Matches if the actual value is truthy.                                 | `(truthy)`              |\n| `falsy`        | Matches if the actual value is falsy.                                  | `(falsy)`               |\n| **Numeric**    |                                                                        |                         |\n| `at-least`     | Matches if actual value is greater or equal than the expected value.   | `(at-least value)`      |\n| `at-most`      | Matches if actual value is less or equal than the expected value.      | `(at-most value)`       |\n| `between`      | Matches if actual value is between the lower and upper bounds.         | `(between lower upper)` |\n| **String**     |                                                                        |                         |\n| `starts-with`  | Matches if actual string starts with the expected prefix.              | `(starts-with prefix)`  |\n| `ends-with`    | Matches if actual string ends with the expected suffix.                | `(ends-with suffix)`    |\n| `includes`     | Matches if actual string includes the expected substring.              | `(includes substring)`  |\n| `regex`        | Matches if the expected regular expression matches the actual string.  | `(regex expression)`    |\n| **Collection** |                                                                        |                         |\n| `contains-all` | Matches if the actual collection contains all expected values.         | `(contains values)`     |\n| `contains-any` | Matches if the actual collection contains any expected values.         | `(contains-any values)` |\n| **Operators**  |                                                                        |                         |\n| `not\u003e`         | Matches if the actual value does not match the provided matcher.       | `(not\u003e matcher)`        |\n| `and\u003e`         | Matches if the actual value matches all provided matchers.             | `(and\u003e m1 m2 ...)`      |\n| `or\u003e`          | Matches if the actual value matches any of the provided matchers.      | `(or\u003e m1 m2 ...)`       |\n\nAll matchers above are available in the `me.pmatiello.mockfn.matchers`\nnamespace.\n\n## Quirks and Limitations\n\nWhile `providing` and `verifying` calls can be nested, all required stubs and\nexpectations for a single mock must be defined in the same call. Mocking a\nfunction in an inner `providing` or `verifying` call will override any\ndefinitions made in the outer scope for the tests being run in the inner scope.\n\n```clj\n(testing \"nested mocks\"\n  (mfn/providing [(one-fn :argument-1) :result-1]\n    (mfn/providing [(one-fn :argument-2) :result-2\n                    (other-fn :argument-3) :result-3]\n      (is (thrown? ExceptionInfo (one-fn :argument-1)))\n      (is (= :result-2 (one-fn :argument-2)))\n      (is (= :result-3 (other-fn :argument-3))))\n    (is (= :result-1 (one-fn :argument-1))))))\n```\n\n## Development\n\nInformation for developing this library.\n\n### Running tests\n\nThe following command will execute the unit tests:\n\n```\n% clj -X:test\n```\n\n### Building\n\nThe following command will build a jar file:\n\n```\n% clj -T:build jar\n```\n\nTo clean a previous build, run:\n\n```\n% clj -T:build clean\n```\n\n### Releasing\n\nBefore releasing, update the library version in the [build.clj](./build.clj)\nfile.\n\nMake a commit and generate a new tag:\n\n```\n% git commit -a -m \"Release: ${VERSION}\"\n% git tag -a \"v${VERSION}\" -m \"Release: ${VERSION}\"\n% git push\n% git push origin \"v${VERSION}\" \n```\n\nTo release to [clojars](https://clojars.org), run:\n\n```\n% mvn deploy:deploy-file \\\n      -Dfile=target/mockfn-${VERSION}.jar \\\n      -DrepositoryId=clojars \\\n      -Durl=https://clojars.org/repo \\\n      -DpomFile=target/classes/META-INF/maven/me.pmatiello/mockfn/pom.xml\n```\n\nNotice that this step requires clojars to be configured as a server in the local\n`~/.m2/settings.xml` file.\n\n## Contribution Policy\n\nThis software is open-source, but closed to contributions.\n\n## License\n\nDistributed under the Eclipse Public License either version 2.0 or (at your\noption) any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpmatiello%2Fmockfn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpmatiello%2Fmockfn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpmatiello%2Fmockfn/lists"}