Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/tlux/delx

Defdelegate on steroids! An Elixir library to make function delegation testable.
https://github.com/tlux/delx

assertions defdelegate delegated-functions elixir elixir-library hex-package mox

Last synced: 5 days ago
JSON representation

Defdelegate on steroids! An Elixir library to make function delegation testable.

Awesome Lists containing this project

README

        

# Delx

[![Build Status](https://travis-ci.org/tlux/delx.svg?branch=master)](https://travis-ci.org/tlux/delx)
[![Coverage Status](https://coveralls.io/repos/github/tlux/delx/badge.svg?branch=master)](https://coveralls.io/github/tlux/delx?branch=master)
[![Hex.pm](https://img.shields.io/hexpm/v/delx.svg)](https://hex.pm/packages/delx)

[Defdelegate](https://hexdocs.pm/elixir/Kernel.html#defdelegate/2) on steroids!
An Elixir library to make function delegation testable.

## Prerequisites

- Erlang 20 or greater
- Elixir 1.8 or greater

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `delx` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[
{:delx, "~> 3.0"}
]
end
```

## Usage

Check out the full docs at [https://hexdocs.pm/delx](https://hexdocs.pm/delx).

Let's say you have the following module.

```elixir
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.

```elixir
defmodule Greeter do
use Delx, otp_app: :greeter

defdelegate hello(name), to: Greeter.StringGreeter
end

Greeter.hello("Tobi")
# => "Hello, Tobi!"
```

## 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`:

```elixir
config :greeter, Delx, mock: true
```

Then in your tests, you can import `Delx.TestAssertions` and use the
`assert_delegate/2` and `refute_delegate/2` assertions.

```elixir
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](https://hexdocs.pm/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):

```elixir
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.

```elixir
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:

```elixir
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.

## Migration from v2 to v3

Note that the function `defdel/2` has been removed in favor of `defdelegate/2`
to give a better experience with the tooling. Additionally, let's say you want
to migrate away from Delx you can simply un`use` Delx to fall back to the
default `Kernel.defdelegate/2` and your code will still work.

The config option to enable testing mode has changed from `stub` to `mock`. If
you got the following line in your config:

```elixir
config :greeter, Delx, stub: true
```

Please change it to:

```elixir
config :greeter, Delx, mock: true
```