{"id":17130739,"url":"https://github.com/jechol/definject","last_synced_at":"2026-04-01T23:01:15.365Z","repository":{"id":43334609,"uuid":"243678697","full_name":"jechol/definject","owner":"jechol","description":"Unobtrusive Dependency Injector for Elixir","archived":false,"fork":false,"pushed_at":"2024-04-10T12:01:58.000Z","size":178,"stargazers_count":56,"open_issues_count":5,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-29T16:55:09.382Z","etag":null,"topics":["dependency-injection","di","elixir","hacktoberfest","mock","unobtrusive"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/jechol.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2020-02-28T04:32:22.000Z","updated_at":"2024-10-08T04:41:06.000Z","dependencies_parsed_at":"2024-05-01T17:21:15.553Z","dependency_job_id":"cb739229-578d-4ff7-a7bf-574eea849e76","html_url":"https://github.com/jechol/definject","commit_stats":{"total_commits":152,"total_committers":6,"mean_commits":"25.333333333333332","dds":0.06578947368421051,"last_synced_commit":"39efb246e8095c53ded60a4df5d0937edcb0029c"},"previous_names":["definject/definject"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/jechol/definject","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fdefinject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fdefinject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fdefinject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fdefinject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jechol","download_url":"https://codeload.github.com/jechol/definject/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fdefinject/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31292781,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: 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":["dependency-injection","di","elixir","hacktoberfest","mock","unobtrusive"],"created_at":"2024-10-14T19:13:08.244Z","updated_at":"2026-04-01T23:01:15.313Z","avatar_url":"https://github.com/jechol.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](https://github.com/jechol/definject/blob/master/brand/logo.png?raw=true)\n\n[![mix test](https://github.com/jechol/definject/workflows/mix%20test/badge.svg)](https://github.com/jechol/definject/actions)\n[![Hex version badge](https://img.shields.io/hexpm/v/definject.svg)](https://hex.pm/packages/definject)\n[![License badge](https://img.shields.io/hexpm/l/definject.svg)](https://github.com/jechol/definject/blob/master/LICENSE.md)\n\nUnobtrusive Dependency Injector for Elixir\n\n## Why?\n\nLet's say we want to test following function.\n\n```elixir\ndef send_welcome_email(user_id) do\n  %{email: email} = Repo.get(User, user_id)\n\n  welcome_email(to: email)\n  |\u003e Mailer.send()\nend\n```\n\nHere's one possible solution to replace `Repo.get/2` and `Mailer.send/1` with mocks:\n\n```elixir\ndef send_welcome_email(user_id, repo \\\\ Repo, mailer \\\\ Mailer) do\n  %{email: email} = repo.get(User, user_id)\n\n  welcome_email(to: email)\n  |\u003e mailer.send()\nend\n```\n\nFirst, I believe that this approach is too obtrusive as it requires modifying the function body to make it testable. Second, with `Mailer` replaced with `mailer`, the compiler no longer check the existence of `Mailer.send/1`.\n\n`definject` does not require you to modify function arguments or body. It allows injecting different mocks to each function. It also does not limit using `:async` option as mocks are contained in each test function.\n\n## Installation\n\nThe package can be installed by adding `definject` to your list of dependencies\nin `mix.exs`:\n\n```elixir\ndef deps do\n  [{:definject, \"~\u003e 1.2\"}]\nend\n```\n\nBy default, `definject` is replaced with `def` in all but the test environment. Add the below configuration to enable in other environments.\n\n```elixir\nconfig :definject, :enable, true\n```\n\nTo format `definject` like `def`, add following to your `.formatter.exs`\n\n```elixir\nlocals_without_parens: [definject: 1, definject: 2]\n```\n\n## Documentation\n\nAPI documentation is available at [https://hexdocs.pm/definject](https://hexdocs.pm/definject)\n\n## Usage\n\n### use Definject\n\n`use Definject` transforms `def` to accept a extra argument `deps` where dependent functions and modules can be injected.\n\n```elixir\nuse Definject\n\ndef send_welcome_email(user_id) do\n  %{email: email} = Repo.get(User, user_id)\n\n  welcome_email(to: email)\n  |\u003e Mailer.send()\nend\n```\n\nis expanded into\n\n```elixir\ndef send_welcome_email(user_id, deps \\\\ %{}) do\n  %{email: email} =\n    Map.get(deps, \u0026Repo.get/2,\n      :erlang.make_fun(Map.get(deps, Repo, Repo), :get, 2)\n    ).(User, user_id)\n\n  welcome_email(to: email)\n  |\u003e Map.get(deps, \u0026Mailer.send/1,\n       :erlang.make_fun(Map.get(deps, Mailer, Mailer), :send, 1)\n     ).()\nend\n```\n\nNote that local function calls like `welcome_email(to: email)` are not expanded unless it is prepended with `__MODULE__`.\n\nNow, you can inject mock functions and modules in tests.\n\n```elixir\ntest \"send_welcome_email\" do\n  Accounts.send_welcome_email(100, %{\n    Repo =\u003e MockRepo,\n    \u0026Mailer.send/1 =\u003e fn %Email{to: \"user100@gmail.com\", subject: \"Welcome\"} -\u003e\n      Process.send(self(), :email_sent)\n    end\n  })\n\n  assert_receive :email_sent\nend\n```\n\nFunction calls raise if the `deps` includes redundant functions or modules.\nYou can disable this by adding `strict: false` option.\n\n```elixir\ntest \"send_welcome_email with strict: false\" do\n  Accounts.send_welcome_email(100, %{\n    \u0026Repo.get/2 =\u003e fn User, 100 -\u003e %User{email: \"user100@gmail.com\"} end,\n    \u0026Repo.all/1 =\u003e fn _ -\u003e [%User{email: \"user100@gmail.com\"}] end, # Unused\n    strict: false\n  })\nend\n```\n\n### mock\n\nIf you don't need pattern matching in mock function, `mock/1` can be used to reduce boilerplates.\n\n```elixir\nimport Definject\n\ntest \"send_welcome_email with mock/1\" do\n  Accounts.send_welcome_email(\n    100,\n    mock(%{\n      Repo =\u003e MockRepo,\n      \u0026Mailer.send/1 =\u003e Process.send(self(), :email_sent)\n    })\n  )\n\n  assert_receive :email_sent\nend\n```\n\nNote that `Process.send(self(), :email_sent)` is surrounded by `fn _ -\u003e end` when expanded.\n\n### import Definject\n\n`import Definject` instead of `use Definject` if you want to manually select functions to inject.\n\n```elixir\nimport Definject\n\ndefinject send_welcome_email(user_id) do\n  %{email: email} = Repo.get(User, user_id)\n\n  welcome_email(to: email)\n  |\u003e Mailer.send()\nend\n```\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE.md) file for details\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjechol%2Fdefinject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjechol%2Fdefinject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjechol%2Fdefinject/lists"}