{"id":13509227,"url":"https://github.com/jjh42/mock","last_synced_at":"2026-02-18T22:02:18.596Z","repository":{"id":8865889,"uuid":"10578183","full_name":"jjh42/mock","owner":"jjh42","description":"Mocking library for Elixir language","archived":false,"fork":false,"pushed_at":"2024-02-20T15:04:00.000Z","size":3296,"stargazers_count":631,"open_issues_count":19,"forks_count":81,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-05-18T13:41:44.236Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elixir","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/jjh42.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"LICENSE.md","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":"2013-06-09T04:13:39.000Z","updated_at":"2024-05-04T14:58:13.000Z","dependencies_parsed_at":"2024-05-01T17:21:22.214Z","dependency_job_id":"28e5a953-e0a4-489b-a116-c42574c617bc","html_url":"https://github.com/jjh42/mock","commit_stats":{"total_commits":102,"total_committers":51,"mean_commits":2.0,"dds":0.7058823529411764,"last_synced_commit":"f3580cb68692b41ee59e9784ba253473dab9420d"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/jjh42/mock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjh42%2Fmock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjh42%2Fmock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjh42%2Fmock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjh42%2Fmock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jjh42","download_url":"https://codeload.github.com/jjh42/mock/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jjh42%2Fmock/sbom","scorecard":{"id":522093,"data":{"date":"2025-08-11","repo":{"name":"github.com/jjh42/mock","commit":"62e583218e5b088eca5c4b17acfff8afca2232c7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":7,"reason":"Found 21/28 approved changesets -- score normalized to 7","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":8,"reason":"2 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-vq52-99r9-h5pw","Warn: Project is vulnerable to: GHSA-9fm9-hp7p-53mf"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 24 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T03:17:57.530Z","repository_id":8865889,"created_at":"2025-08-20T03:17:57.530Z","updated_at":"2025-08-20T03:17:57.530Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29596329,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T20:59:56.587Z","status":"ssl_error","status_checked_at":"2026-02-18T20:58:41.434Z","response_time":162,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2024-08-01T02:01:04.872Z","updated_at":"2026-02-18T22:02:18.572Z","avatar_url":"https://github.com/jjh42.png","language":"Elixir","funding_links":[],"categories":["Testing","Elixir"],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/jjh42/mock.svg?branch=master)](https://travis-ci.org/jjh42/mock)\n\n# Mock\nA mocking library for the Elixir language.\n\nWe use the Erlang [meck library](https://github.com/eproxus/meck) to provide\nmodule mocking functionality for Elixir. It uses macros in Elixir to expose the\nfunctionality in a convenient manner for integrating in Elixir tests.\n\nSee the full [reference documentation](https://hexdocs.pm/mock/Mock.html).\n\n# Table of Contents\n* [Mock](#Mock)\n\t* [Installation](#Installation)\n\t* [*with_mock* - Mocking a single module](#with_mock---Mocking-a-single-module)\n\t* [*with_mocks* - Mocking multiple modules](#with_mocks---Mocking-multiple-modules)\n\t* [*test_with_mock* - with_mock helper](#test_with_mock---with_mock-helper)\n\t* [*setup_with_mocks* - Configure all tests to have the same mocks](#setup_with_mocks---Configure-all-tests-to-have-the-same-mocks)\n\t* [Mocking input dependent output](#Mocking-input-dependent-output)\n\t* [Mocking functions with different arities](#Mocking-functions-with-different-arities)\n\t* [Mocking repeated calls to the same function with different results](#mock-repeated-calls)\n\t* [*passthrough* - partial mocking of a module](#passthrough---partial-mocking-of-a-module)\n\t* [Assert called - assert a specific function was called](#Assert-called---assert-a-specific-function-was-called)\n\t\t* [Assert called - specific value](#Assert-called---specific-value)\n\t\t* [Assert called - wildcard](#Assert-called---wildcard)\n\t\t* [Assert called - pattern matching](#Assert-called---pattern-matching)\n\t\t* [Assert call order](#Assert-call-order)\n\t* [Assert not called - assert a specific function was not called](#Assert-not-called---assert-a-specific-function-was-not-called)\n\t* [Assert called exactly - assert a specific function was called exactly x times](#Assert-called-exactly---assert-a-specific-function-was-called-exactly-x-times)\n\t* [Assert called at least - assert a specific function was called at least x times](#Assert-called-at-least---assert-a-specific-function-was-called-at-least-x-times)\n\t* [NOT SUPPORTED - Mocking internal function calls](#NOT-SUPPORTED---Mocking-internal-function-calls)\n\t* [Tips](#Tips)\n\t* [Help](#Help)\n\t* [Suggestions](#Suggestions)\n\n## Installation\nFirst, add mock to your `mix.exs` dependencies:\n\n```elixir\ndef deps do\n  [{:mock, \"~\u003e 0.3.0\", only: :test}]\nend\n```\n\nand run `$ mix deps.get`.\n\n## *with_mock* - Mocking a single module\nThe Mock library provides the `with_mock` macro for running tests with\nmocks.\n\nFor a simple example, if you wanted to test some code which calls\n`HTTPotion.get` to get a webpage but without actually fetching the\nwebpage you could do something like this:\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"test_name\" do\n    with_mock HTTPotion, [get: fn(_url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end] do\n      assert \"\u003chtml\u003e\u003c/html\u003e\" == HTTPotion.get(\"http://example.com\")\n    end\n  end\nend\n````\n\nThe `with_mock` macro creates a mock module. The keyword list provides a set\nof mock implementation for functions we want to provide in the mock (in\nthis case just `get`). Inside `with_mock` we exercise the test code\nand we can check that the call was made as we expected using `called` and\nproviding the example of the call we expected.\n\n## *with_mocks* - Mocking multiple modules\n\nYou can mock up multiple modules with `with_mocks`.\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"multiple mocks\" do\n    with_mocks([\n      {Map,\n       [],\n       [get: fn(%{}, \"http://example.com\") -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end]},\n      {String,\n       [],\n       [reverse: fn(x) -\u003e 2*x end,\n        length: fn(_x) -\u003e :ok end]}\n    ]) do\n      assert Map.get(%{}, \"http://example.com\") == \"\u003chtml\u003e\u003c/html\u003e\"\n      assert String.reverse(3) == 6\n      assert String.length(3) == :ok\n    end\n  end\nend\n````\n\nThe second parameter of each tuple is `opts` - a list of optional arguments\npassed to meck.\n\n## *test_with_mock* - with_mock helper\n\nAn additional convenience macro `test_with_mock` is supplied which internally\ndelegates to `with_mock`. Allowing the above test to be written as follows:\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test_with_mock \"test_name\", HTTPotion,\n    [get: fn(_url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end] do\n    HTTPotion.get(\"http://example.com\")\n    assert_called HTTPotion.get(\"http://example.com\")\n  end\nend\n````\n\nThe `test_with_mock` macro can also be passed a context argument\nallowing the sharing of information between callbacks and the test\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  setup do\n    doc = \"\u003chtml\u003e\u003c/html\u003e\"\n    {:ok, doc: doc}\n  end\n\n  test_with_mock \"test_with_mock with context\", %{doc: doc}, HTTPotion, [],\n    [get: fn(_url, _headers) -\u003e doc end] do\n\n    HTTPotion.get(\"http://example.com\", [foo: :bar])\n    assert_called HTTPotion.get(\"http://example.com\", :_)\n  end\nend\n````\n\n## *setup_with_mocks* - Configure all tests to have the same mocks\n\nThe `setup_with_mocks` mocks up multiple modules prior to every single test\nalong while calling the provided setup block. It is simply an integration of the\n`with_mocks` macro available in this module along with the [`setup`](https://hexdocs.pm/ex_unit/ExUnit.Callbacks.html#setup/1)\nmacro defined in elixir's `ExUnit`.\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n  import Mock\n\n  setup_with_mocks([\n    {Map, [], [get: fn(%{}, \"http://example.com\") -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end]}\n  ]) do\n    foo = \"bar\"\n    {:ok, foo: foo}\n  end\n\n  test \"setup_with_mocks\" do\n    assert Map.get(%{}, \"http://example.com\") == \"\u003chtml\u003e\u003c/html\u003e\"\n  end\nend\n````\n\nThe behaviour of a mocked module within the setup call can be overridden using any\nof the methods above in the scope of a specific test. Providing this functionality\nby `setup_all` is more difficult, and as such, `setup_all_with_mocks` is not currently\nsupported.\n\nCurrently, mocking modules cannot be done asynchronously, so make sure that you\nare not using `async: true` in any module where you are testing.\n\nAlso, because of the way mock overrides the module, it must be defined in a\nseparate file from the test file.\n\n## Mocking input dependent output\n\nIf you have a function that should return different values depending on what the\ninput is, you can do as follows:\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"mock functions with multiple returns\" do\n    with_mock(Map, [\n      get: fn\n        (%{}, \"http://example.com\") -\u003e \"\u003chtml\u003eHello from example.com\u003c/html\u003e\"\n        (%{}, \"http://example.org\") -\u003e \"\u003chtml\u003eexample.org says hi\u003c/html\u003e\"\n        (%{}, url) -\u003e conditionally_mocked(url)\n      end\n    ]) do\n      assert Map.get(%{}, \"http://example.com\") == \"\u003chtml\u003eHello from example.com\u003c/html\u003e\"\n      assert Map.get(%{}, \"http://example.org\") == \"\u003chtml\u003eexample.org says hi\u003c/html\u003e\"\n      assert Map.get(%{}, \"http://example.xyz\") == \"\u003chtml\u003eHello from example.xyz\u003c/html\u003e\"\n      assert Map.get(%{}, \"http://example.tech\") == \"\u003chtml\u003eexample.tech says hi\u003c/html\u003e\"\n    end\n  end\n\n  def conditionally_mocked(url) do\n    cond do\n      String.contains?(url, \".xyz\") -\u003e \"\u003chtml\u003eHello from example.xyz\u003c/html\u003e\"\n      String.contains?(url, \".tech\") -\u003e \"\u003chtml\u003eexample.tech says hi\u003c/html\u003e\"\n    end\n  end\nend\n````\n\n## Mocking functions with different arities\n\nYou can mock functions in the same module with different arity:\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"mock functions with different arity\" do\n    with_mock String,\n      [slice: fn(string, range)      -\u003e string end,\n       slice: fn(string, range, len) -\u003e string end]\n    do\n      assert String.slice(\"test\", 1..3) == \"test\"\n      assert String.slice(\"test\", 1, 3) == \"test\"\n    end\n  end\nend\n\n````\n\n## Mock repeated calls\n\nYou can mock repeated calls to the same function _and_ arguments to return\ndifferent results in a series using the `in_series` call with static values.\nThis does not currently support _functions_.\n\n**Caution**: This is only useful in rare instances where the underlying business\nlogic is likely to be stateful. If you can avoid it by using different function\narguments, or refactor the function to be stateful, consider that approach first.\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"mock repeated calls with in_series\" do\n    with_mock String,\n      [slice: [in_series([\"test\", 1..3], [\"string1\", \"string2\", \"string3\"])]]\n    do\n      assert String.slice(\"test\", 1..3) == \"string1\"\n      assert String.slice(\"test\", 1..3) == \"string2\"\n      assert String.slice(\"test\", 1..3) == \"string3\"\n    end\n  end\nend\n\n````\n\n## *passthrough* - partial mocking of a module\n\nBy default, only the functions being mocked can be accessed from within the test.\nTrying to call a non-mocked function from a mocked Module will result in an error.\nThis can be circumvented by passing the `:passthrough` option like so:\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n  import Mock\n\n  test_with_mock \"test_name\", IO, [:passthrough], [] do\n    IO.puts \"hello\"\n    assert_called IO.puts \"hello\"\n  end\nend\n````\n\n## Assert called - assert a specific function was called\n\nYou can check whether or not your mocked module was called.\n\n### Assert called - specific value\n\nIt is possible to assert that the mocked module was called with a specific input.\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"test_name\" do\n    with_mock HTTPotion, [get: fn(_url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end] do\n      HTTPotion.get(\"http://example.com\")\n      assert_called HTTPotion.get(\"http://example.com\")\n    end\n  end\nend\n````\n\n### Assert called - wildcard\n\nIt is also possible to assert that the mocked module was called with any value\nby passing the `:_` wildcard.\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"test_name\" do\n    with_mock HTTPotion, [get: fn(_url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end] do\n      HTTPotion.get(\"http://example.com\")\n      assert_called HTTPotion.get(:_)\n    end\n  end\nend\n````\n\n### Assert called - pattern matching\n\n`assert_called` will check argument equality using `==` semantics, not pattern matching.\nFor structs, you must provide every property present on the argument as it was called or\nit will fail. To use pattern matching (useful when you only care about a few properties on\nthe argument or need to perform advanced matching like regex matching), provide custom\nargument matcher(s) using [`:meck.is/1`](https://hexdocs.pm/meck/meck.html#is-1).\n\n```` elixir\ndefmodule User do\n  defstruct [:id, :name, :email]\nend\n\ndefmodule Network do\n  def update(%User{} = user), do: # ...\nend\n\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"test_name\" do\n    with_mock Network, [update: fn(_user) -\u003e :ok end] do\n      user = %User{id: 1, name: \"Jane Doe\", email: \"jane.doe@gmail.com\"}\n      Network.update(user)\n\n      assert_called Network.update(\n        :meck.is(fn user -\u003e\n          assert user.__struct__ == User\n          assert user.id == 1\n\n          # matcher must return true when the match succeeds\n          true\n        end)\n      )\n    end\n  end\nend\n````\n\n## Assert not called - assert a specific function was not called\n\n`assert_not_called` will assert that a mocked function was not called.\n\n```elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"test_name\" do\n    with_mock HTTPotion, [get: fn(_url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end] do\n      # Using Wildcard\n      assert_not_called HTTPotion.get(:_)\n\n      HTTPotion.get(\"http://example.com\")\n\n      # Using Specific Value\n      assert_not_called HTTPotion.get(\"http://another-example.com\")\n    end\n  end\nend\n```\n\n## Assert called exactly - assert a specific function was called exactly x times\n\n`assert_called_exactly` will assert that a mocked function was called exactly the expected number of times.\n\n```elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"test_name\" do\n    with_mock HTTPotion, [get: fn(_url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end] do\n      HTTPotion.get(\"http://example.com\")\n      HTTPotion.get(\"http://example.com\")\n\n      # Using Wildcard\n      assert_called_exactly HTTPotion.get(:_), 2\n\n      # Using Specific Value\n      assert_called_exactly HTTPotion.get(\"http://example.com\"), 2\n    end\n  end\nend\n```\n\n## Assert called at least - assert a specific function was called at least x times\n\n`assert_called_at_least` will assert that a mocked function was called at least the expected number of times.\n\n```elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"test_name\" do\n    with_mock HTTPotion, [get: fn(_url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end] do\n      HTTPotion.get(\"http://example.com\")\n      HTTPotion.get(\"http://example.com\")\n      HTTPotion.get(\"http://example.com\")\n\n      # Using Wildcard\n      assert_called_at_least HTTPotion.get(:_), 2\n\n      # Using Specific Value\n      assert_called_at_least HTTPotion.get(\"http://example.com\"), 2\n    end\n  end\nend\n```\n\n\n\n### Assert call order\n\n`call_history` will return the `meck.history(Module)` allowing you assert on the order of the function invocation:\n\n```elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"test_name\" do\n    with_mock HTTPotion, [get: fn(_url) -\u003e \"\u003chtml\u003e\u003c/html\u003e\" end] do\n      HTTPotion.get(\"http://example.com\")\n\n      assert call_history(HTTPotion) ==\n        [\n          {pid, {HTTPotion, :get, [\"http://example.com\"]}, \"\u003chtml\u003e\u003c/html\u003e\"}\n        ]\n    end\n  end\nend\n```\n\n\nYou can use any valid Elixir pattern matching/multiple function heads to accomplish\nthis more succinctly, but remember that the matcher will be executed for _all_ function\ncalls, so be sure to include a fallback case that returns `false`. For mocked functions\nwith multiple arguments, you must include a matcher/pattern for each argument.\n\n```` elixir\ndefmodule Network.V2 do\n  def update(%User{} = user, changes), do: # ...\n\n  def update(id, changes) when is_integer(id), do: # ...\n\n  def update(_, _), do: # ...\nend\n\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n\n  import Mock\n\n  test \"test_name\" do\n    with_mock Network.V2, [update: fn(_user, _changes) -\u003e :ok end] do\n      Network.V2.update(%User{id: 456, name: \"Jane Doe\"}, %{name: \"John Doe\"})\n      Network.V2.update(123, %{name: \"John Doe\", email: \"john.doe@gmail.com\"})\n      Network.V2.update(nil, %{})\n\n      # assert that `update` was called with user id 456\n      assert_called Network.V2.update(\n        :meck.is(fn\n          %User{id: 456} -\u003e true\n          _ -\u003e false\n        end),\n        :_\n      )\n\n      # assert that `update` was called with an email change\n      assert_called Network.V2.update(\n        :_,\n        :meck.is(fn\n          %{email: \"john.doe@gmail.com\"} -\u003e true\n          _ -\u003e false\n        end)\n      )\n    end\n  end\nend\n````\n\n## NOT SUPPORTED\n\n### Mocking internal function calls\n\nA common issue a lot of developers run into is Mock's lack of support for mocking\ninternal functions. Mock will behave as follows:\n\n```` elixir\ndefmodule MyApp.IndirectMod do\n\n  def value do\n    1\n  end\n\n  def indirect_value do\n    value()\n  end\n\n  def indirect_value_2 do\n    MyApp.IndirectMod.value()\n  end\n\nend\n````\n\n```` elixir\ndefmodule MyTest do\n  use ExUnit.Case, async: false\n  import Mock\n\n  test \"indirect mock\" do\n    with_mocks([\n      { MyApp.IndirectMod, [:passthrough], [value: fn -\u003e 2 end] },\n    ]) do\n      # The following assert succeeds\n      assert MyApp.IndirectMod.indirect_value_2() == 2\n      # The following assert also succeeds\n      assert MyApp.IndirectMod.indirect_value() == 1\n    end\n  end\nend\n````\n\nIt is important to understand that only fully qualified function calls get mocked.\nThe reason for this is because of the way Meck is structured. Meck creates a thin wrapper module with the name of the mocked module (and passes through any calls to the original\nModule in case passthrough is used). The original module is renamed, but otherwise unmodified. Once the call enters the original module, the local function call jumps stay in the module.\n\nBig thanks to @eproxus (author of Meck) who helped explain this to me. We're looking\ninto some alternatives to help solve this, but it is something to be aware of in the meantime. The issue is being tracked in [Issue 71](https://github.com/jjh42/mock/issues/71).\n\nIn order to workaround this issue, the `indirect_value` can be rewritten like so:\n```` elixir\n  def indirect_value do\n    __MODULE__.value()\n  end\n````\n\nOr, like so:\n\n```` elixir\n  def indirect_value do\n    MyApp.IndirectMod.value()\n  end\n````\n\n### Mocking macros\n\nCurrently mocking macros is not supported. For example this will not work because `Logger.error/1` is a macro:\n\n```elixir\nwith_mock Logger, [error: fn(_) -\u003e 42 end] do\n   assert Logger.error(\"msg\") == 42\nend\n```\n\nThis code will give you this error: `Erlang error: {:undefined_function, {Logger, :error, 1}}`\n\nAs a workaround, you may define a wrapper function for the macro you need to invoke:\n\n```elixir\ndefmodule MyModule do\n  def log_error(arg) do\n    Logger.error(arg)\n  end\nend\n```\n\nThen in your test you can mock that wrapper function:\n\n```elixir\nwith_mock MyModule, [log_error: fn(_) -\u003e 42 end] do\n   assert MyModule.log_error(\"msg\") == 42\nend\n```\n\n## Tips\nThe use of mocking can be somewhat controversial. I personally think that it\nworks well for certain types of tests. Certainly, you should not overuse it. It\nis best to write as much as possible of your code as pure functions which don't\nrequire mocking to test. However, when interacting with the real world (or web\nservices, users etc.) sometimes side-effects are necessary. In these cases,\nmocking is one useful approach for testing this functionality.\n\nAlso, note that Mock has a global effect so if you are using Mocks in multiple\ntests set `async: false` so that only one test runs at a time.\n\n## Help\nOpen an issue.\n\n## Publishing New Package Versions\nFor library maintainers, the following is an example of how to publish new versions of the package. Run the following commands assuming you incremented the version in the `mix.exs` file from 0.3.4 to 0.3.5:\n\n```\ngit commit -am \"Increase version from 0.3.4 to 0.3.5\"\ngit tag -a v0.3.5 -m \"Git tag 0.3.5\"\ngit push origin --tags\nmix hex.publish\n```\n\n## Suggestions\nI'd welcome suggestions for improvements or bugfixes. Just open an issue.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjjh42%2Fmock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjjh42%2Fmock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjjh42%2Fmock/lists"}