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 three options: - `{:behaviour, behaviour_name}`: override the default behaviour name (by default Moxable will use `.Behaviour`) - {:behaviours, list_of_behaviours}: a list of behaviour modules to be used - `{: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__) behaviours = cond do Keyword.has_key?(unquote(opts), :behaviour) -> [Keyword.fetch!(unquote(opts), :behaviour)] Keyword.has_key?(unquote(opts), :behaviours) -> Keyword.fetch!(unquote(opts), :behaviours) true -> [(module ++ ["Behaviour"]) |> Module.concat()] end for behaviour <- behaviours do @behaviour behaviour end @mock_for (["Moxable"] ++ module) |> Module.concat() if Mix.env() == :test do Mox.defmock(@mock_for, for: behaviours) 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