README.md

# ExUnit.LetLazy

For those coming from RSpec and missing `let`.

## Examples

### Order does not matter

In the following example, note the way it's not important what order you call
`let` in. The value you provide is a macro expression evaluated when it is used
via `get`.

```elixir
defmodule AddTest do
  use ExUnit.Case, async: true
  use ExUnit.LetLazy

  # code under test
  defp add(a, b) do
    a + b
  end

  describe "add/2 with a = b, b = 15" do
    # order is not important, thanks Elixir macros
    let :a, get(:b)
    let :b, 15

    test "gives the correct answer" do
      assert add(get(:a), get(:b)) == 30
    end
  end
end
```

This is implemented using ETS.

* `let` stores a function expression
* `get` pulls it out and calls it

### Each test is a mashup of bits of context

Like in RSpec, you can use `let` to keep the LOC in your test files manageable.
Consider this example. Instead of repeating the setup each time with slight
variations, all the possible options for arguments are placed at the top, and
inside each describe block, we pull in the combination that we want to test.

What's more, we can re-use the exact same context easily in order to test
different qualities of our function - e.g. one test to make assertions about the
messages it sends, a separate test to make assertions about its return value.

```elixir
defmodule ResuseTest do
  use ExUnit.Case, async: true
  use ExUnit.LetLazy

  let :a_case_1, %{large_data_structure_1a: "x"}
  let :a_case_2, %{large_data_structure_2a: "x"}
  let :b_case_1, %{large_data_structure_1b: "x"}
  let :b_case_2, %{large_data_structure_2b: "x"}
  let :call, my_function(get(:a), get(:b))

  # code under test

  defp my_function(a, a) do
    send self(), {:log, :error, :same}
    a
  end
  defp my_function(a, b) do
    Map.merge(a, b)
  end


  describe "my_function, a = b" do
    let :a, get(:a_case_1)
    let :b, get(:a)

    test "gives the correct answer" do
      assert get(:call) == %{large_data_structure_1a: "x"}
    end

    test "sends the right message" do
      get(:call)
      assert_received {:log, :error, :same}
    end
  end

  describe "my_function, a != b" do
    let :a, get(:a_case_1)
    let :b, get(:b_case_1)

    test "does not send the message" do
      get(:call)
      refute_received {:log, :error, _}
    end
  end

  describe "my_function, a case 1, b case 1" do
    let :a, get(:a_case_1)
    let :b, get(:b_case_1)

    test "gives the correct answer" do
      assert get(:call) == %{large_data_structure_1a: "x", large_data_structure_1b: "x"}
    end
  end

  describe "my_function, a case 1, b case 2" do
    let :a, get(:a_case_1)
    let :b, get(:b_case_2)

    test "gives the correct answer" do
      assert get(:call) == %{large_data_structure_1a: "x", large_data_structure_2b: "x"}
    end
  end
  
  # ... other combinations of a, b, and assertion ...
end
```

## Installation

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

```elixir
def deps do
  [
    {:ex_unit_let_lazy, "~> 0.1.0"}
  ]
end
```

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/ex_unit_let_lazy](https://hexdocs.pm/ex_unit_let_lazy).

## See also

See also [ExUnit.Let] which is similar, but simpler, uses the test context
structure, and thus relies on the ordering of `let` calls.

[ExUnit.Let]: https://github.com/rzane/ex_unit_let