defmodule Moxable do
@moduledoc """
Moxable is a simplified method for working with Mox in an Elixir project.
## Setup
Add `use Moxable` to any module you want to use Mox with in testing and define a behaviour
for your module. `use Mockable` takes two options:
- `{:behaviour, behaviour_name}`: override the default behaviour name (by default Moxable
will use `<YOUR_MODULE_NAME>.Behaviour`)
- `{:dev_impl, dev_module}`: override the module that will be used in the `:dev` context
## Usage
Anywhere you will be calling or `expect`ing mocked functions of your Moxable'd module,
`use` it to define a dynamic alias to the real, mocked or dev version of the module based
on the current context. `:as` may be supplied as an option to override the alias name.
"""
defmacro __using__(opts) do
quote do
module = Module.split(__MODULE__)
default_behaviour = (module ++ ["Behaviour"]) |> Module.concat()
@behaviour Keyword.get(unquote(opts), :behaviour, default_behaviour)
@mock_for (["Moxable"] ++ module) |> Module.concat()
if Mix.env() == :test do
Mox.defmock(@mock_for, for: @behaviour)
end
@default_module_alias Module.concat([List.last(module)])
@implementation (case Mix.env() do
:test -> @mock_for
:dev -> Keyword.get(unquote(opts), :dev_impl, __MODULE__)
_ -> __MODULE__
end)
defmacro __using__(opts) do
quote do
alias unquote(@implementation),
as: unquote(Keyword.get(opts, :as, @default_module_alias))
end
end
end
end
end