Delx v3.0.0 Delx View Source
An Elixir library to make function delegation testable.
Usage
Let's say you have the following module.
iex> defmodule Greeter.StringGreeter do
...> def hello(name) do
...> "Hello, #{name}!"
...> end
...> end
You can delegate functions calls to another module by using the Delx
module and calling the defdelegate/2
macro in the module body. It has
the same API as Elixir's own Kernel.defdelegate/2
macro.
iex> defmodule Greeter do
...> use Delx, otp_app: :greeter
...> defdelegate hello(name), to: Greeter.StringGreeter
...> end
iex> Greeter.hello("Tobi")
"Hello, Tobi!"
The reason you need to use Delx
and define the :otp_app
option is that
each application can configure their own delegation behavior. So third-party
libaries that also use Delx remain unaffected of your application-specific
configuration.
Testing
One great benefit of Delx is that you can test delegation without invoking the actual implementation of the delegation target, thus eliminating all side effects.
Built-In Assertions
Delx brings it's own test assertions.
All you need to do is to activate delegation mocking for your test
environment by putting the following line in your config/test.exs
:
config :greeter, Delx, mock: true
Then in your tests, you can import Delx.TestAssertions
and use the
Delx.TestAssertions.assert_delegate/2
and
Delx.TestAssertions.refute_delegate/2
assertions.
defmodule GreeterTest do
use ExUnit.Case
import Delx.TestAssertions
describe "hello/1" do
test "delegate to Greeter.StringGreeter" do
assert_delegate {Greeter, :hello, 1}, to: Greeter.StringGreeter
end
end
end
Note that once you activate mocking all delegated functions do not return
anymore but instead raise the Delx.MockedDelegationError
. If you really
want to call the original implementation, you have to avoid any calls of
delegated functions.
With Mox
If you are using Mox in your application you have another possibility to test delegated functions.
Register a mock for the Delx.Delegator
behavior to your
test/test_helper.exs
(or wherever you define your mocks):
Mox.defmock(Greeter.DelegatorMock, for: Delx.Delegator)
Then, in your config/test.exs
you have to set the mock as delegator module
for your app.
config :my_app, Delx, delegator: Greeter.DelegatorMock
Please make sure not to use the :mock
option and a :delegator
option at
the same time as this may lead to unexpected behavior.
Now you are able to expect
calls to delegated functions:
defmodule GreeterTest do
use ExUnit.Case
import Mox
setup :verify_on_exit!
describe "hello/1" do
test "delegate to Greeter.StringGreeter" do
expect(
Greeter.DelegatorMock,
:apply,
fn {Greeter, :hello},
{Greeter.StringGreeter, :hello},
["Tobi"] ->
:ok
end
)
Greeter.hello("Tobi")
end
end
end
For more information on how to implement your own delegator, refer to the
docs of the Delx.Delegator
behavior.
Note that the configuration is only applied at compile time, so you are unable to mock or replace the delegator module at runtime.