definject v0.2.0 Definject View Source
Link to this section Summary
Functions
definject
transforms a function to accept a map where dependent functions can be injected.
Link to this section Functions
definject
transforms a function to accept a map where dependent functions can be injected.
import Definject
definject send_welcome_email(user_id) do
%{email: email} = Repo.get(User, user_id)
Email.welcome(email)
|> Mailer.send()
end
is expanded into
def send_welcome_email(user_id, deps \\ %{}) do
%{email: email} = (deps[{Repo, :get, 2}] || &Repo.get/2).(User, user_id)
(deps[{Email, :welcome, 1}] || &Email.welcome/1).(email)
|> (deps[{Mailer, :send, 1}] || &Mailer.send/1).()
end
Then you can inject mock functions in tests.
test "send_welcome_email" do
Accounts.send_welcome_email(100, %{
{Repo, :get, 2} => fn User, 100 -> %User{email: "mr.jechol@gmail.com"} end,
{Mailer, :send, 1} => fn %Email{to: "mr.jechol@gmail.com", subject: "Welcome"} ->
Process.send(self(), :email_sent)
end
})
assert_receive :email_sent
end
definject
raises if the passed map includes a function that's not called within the injected function.
You can disable this by adding strict: false
option.
test "send_welcome_email with strict: false" do
Accounts.send_welcome_email(100, %{
{Repo, :get, 2} => fn User, 100 -> %User{email: "mr.jechol@gmail.com"} end,
{Repo, :all, 1} => fn _ -> [%User{email: "mr.jechol@gmail.com"}] end, # Unused
:strict => false,
})
end
If you don't need pattern matching in mock function, mock/1
can be used to reduce boilerplates.
test "send_welcome_email with mock/1" do
Accounts.send_welcome_email(
100,
mock(%{
&Repo.get/2 => %User{email: "mr.jechol@gmail.com"},
&Mailer.send/1 => Process.send(self(), :email_sent)
})
)
assert_receive :email_sent
end
Note that Process.send(self(), :email_sent)
is surrounded by fn _ -> end
when expanded.