defmodule Rewire do @moduledoc """ Rewire is a libary for replacing hard-wired dependencies of the module your unit testing. This keeps your production code free from any unit testing-specific concerns. ## Usage Given a module such as this: ```elixir # this module has a hard-wired dependency on the `English` module defmodule Conversation do def start(), do: English.greet() end ``` If you define a `mox` mock `EnglishMock` you can rewire the dependency in your unit test: ```elixir defmodule MyTest do use ExUnit.Case use Rewire # (1) activate `rewire` import Mox rewire Conversation, English: EnglishMock # (2) rewire `English` to `EnglishMock` test "start/0" do stub(EnglishMock, :greet, fn -> "g'day" end) assert Conversation.start() == "g'day" # (3) test using the mock end end ``` You can also give the alias a different name using `as`: ```elixir rewire Conversation, English: EnglishMock, as: SmallTalk ``` Alternatively, you can also rewire a module on a test-by-test basis: ```elixir defmodule MyTest do use ExUnit.Case use Rewire # (1) activate `rewire` import Mox test "start/0" do rewire Conversation, English: EnglishMock do # (2) only rewired inside the block stub(EnglishMock, :greet, fn -> "g'day" end) assert Conversation.start() == "g'day" # (3) test using the mock end end end ``` This example uses `mox`, but `rewire` is mocking library-agnostic. """ import Rewire.Utils defmacro __using__(_) do quote do # Needed for importing the `rewire` macro. import Rewire end end @doc """ Macro that allows to rewire (and alias) a module. ```elixir use Rewire rewire App.ModuleToRewire, ModuleDep: Mock # `ModuleToRewire` will use `Mock` now end ``` ## Options `opts` is a keyword list: * `as` - give the rewired module a different name * any other item, like `ModuleDep: Mock`, will be interpreted as a mapping from one module to another """ defmacro rewire({:__aliases__, _, rewire_module_ast}, opts) do %{aliases: aliases} = __CALLER__ opts = parse_opts(rewire_module_ast, opts, aliases) Rewire.Alias.rewire_alias(opts) end @doc """ Macro that allows to rewire a module within a block. ```elixir use Rewire rewire App.ModuleToRewire, ModuleDep: Mock do # `ModuleToRewire` will use `Mock` now end ``` See `rewire/2` for a description of options. """ defmacro rewire({:__aliases__, _, rewire_module_ast}, opts, do: block) do %{aliases: aliases} = __CALLER__ opts = parse_opts(rewire_module_ast, opts, aliases) Rewire.Block.rewire_block(opts, block) end end