# 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