Skip to content

Commit 1f491df

Browse files
author
José Valim
committed
Add support to case templates
1 parent 8ca4517 commit 1f491df

File tree

6 files changed

+160
-24
lines changed

6 files changed

+160
-24
lines changed

lib/ex_unit/lib/ex_unit.ex

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ defmodule ExUnit do
3535
3636
bin/elixir assertion_test.exs
3737
38-
## Assertions
38+
## Case, callbacks and assertions
3939
40-
Check ExUnit.Assertions for assertions documentation.
40+
Check `ExUnit.Case` and `ExUnit.Callbacks` for more information about
41+
defining test cases.
42+
43+
The `ExUnit.Assertions` module contains a set of macros to easily
44+
generate assertions with appropriate error messages.
4145
4246
## User config
4347

lib/ex_unit/lib/ex_unit/callbacks.ex

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ defmodule ExUnit.Callbacks do
3434
"""
3535

3636
@doc false
37-
defmacro __using__(_) do
37+
defmacro __using__(opts) do
38+
parent = opts[:parent]
39+
3840
quote do
3941
@exunit_setup []
4042
@exunit_teardown []
@@ -45,23 +47,27 @@ defmodule ExUnit.Callbacks do
4547
@after_compile unquote(__MODULE__)
4648
import unquote(__MODULE__)
4749

50+
def __exunit__(:parent) do
51+
unquote(parent)
52+
end
53+
4854
def __exunit__(:setup, context) do
49-
context = __exunit_setup__(context)
55+
context = __exunit_setup__ unquote(parent).__exunit__(:setup, context)
5056
ExUnit.Callbacks.__setup__(__MODULE__, context)
5157
end
5258

5359
def __exunit__(:teardown, context) do
54-
context = __exunit_teardown__(context)
60+
context = unquote(parent).__exunit__(:teardown, __exunit_teardown__ context)
5561
ExUnit.Callbacks.__teardown__(__MODULE__, context)
5662
end
5763

5864
def __exunit__(:setup_all, context) do
59-
context = __exunit_setup_all__(context)
65+
context = __exunit_setup_all__ unquote(parent).__exunit__(:setup_all, context)
6066
ExUnit.Callbacks.__setup_all__(__MODULE__, context)
6167
end
6268

6369
def __exunit__(:teardown_all, context) do
64-
context = __exunit_teardown_all__(context)
70+
context = unquote(parent).__exunit__(:teardown_all, __exunit_teardown_all__ context)
6571
ExUnit.Callbacks.__teardown_all__(__MODULE__, context)
6672
end
6773
end

lib/ex_unit/lib/ex_unit/case.ex

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ defmodule ExUnit.Case do
2626

2727
@doc false
2828
defmacro __using__(opts // []) do
29-
async = Keyword.get(opts, :async, false)
29+
async = Keyword.get(opts, :async, false)
30+
parent = Keyword.get(opts, :parent, __MODULE__)
3031

3132
quote do
3233
if unquote(async) do
@@ -35,12 +36,18 @@ defmodule ExUnit.Case do
3536
ExUnit.Server.add_sync_case(__MODULE__)
3637
end
3738

39+
use ExUnit.Callbacks, parent: unquote(parent)
40+
3841
import ExUnit.Assertions
3942
import ExUnit.Case
40-
use ExUnit.Callbacks
4143
end
4244
end
4345

46+
@doc false
47+
def __exunit__(kind, context) when kind in [:setup, :teardown, :setup_all, :teardown_all] do
48+
context
49+
end
50+
4451
@doc """
4552
Provides a convenient macro that allows a test to be
4653
defined with a string. This macro automatically inserts
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
defmodule ExUnit.CaseTemplate do
2+
@moduledoc """
3+
This module allows a developer to define a test case
4+
template to be used throughout his tests. This is useful
5+
when there are a set of functions that should be shared
6+
between tests or a set of setup/teardown callbacks.
7+
8+
By using this module, the callbacks and assertions
9+
available for regular test cases will also be available.
10+
11+
## Example
12+
13+
defmodule MyCase do
14+
use ExUnit.CaseTemplate
15+
16+
setup do
17+
IO.puts "This will run before each test that uses this case"
18+
end
19+
end
20+
21+
defmodule MyTest do
22+
use MyCase, async: true
23+
24+
test "truth" do
25+
assert true
26+
end
27+
end
28+
29+
"""
30+
31+
@doc false
32+
defmacro __using__(_) do
33+
quote do
34+
use ExUnit.Callbacks, parent: ExUnit.Case
35+
36+
import unquote(__MODULE__)
37+
import ExUnit.Assertions
38+
39+
defmacro __using__(opts) do
40+
unquote(__MODULE__).__parent__(__MODULE__, opts)
41+
end
42+
43+
defoverridable [__using__: 1]
44+
end
45+
end
46+
47+
@doc false
48+
def __parent__(module, opts) do
49+
opts = Keyword.put(opts, :parent, module)
50+
51+
quote do
52+
use ExUnit.Case, unquote(opts)
53+
end
54+
end
55+
56+
@doc """
57+
Allows a developer to code to be invoked when
58+
this module is used.
59+
"""
60+
defmacro using(var // quote(do: _), do: block) do
61+
quote location: :keep do
62+
defmacro __using__(unquote(var) = opts) do
63+
parent = unquote(__MODULE__).__parent__(__MODULE__, opts)
64+
result = unquote(block)
65+
{ :__block__, [], [parent, result] }
66+
end
67+
end
68+
end
69+
end
Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
Code.require_file "../../test_helper.exs", __FILE__
22

33
defmodule ExUnit.CallbacksTest do
4-
use ExUnit.Case, sync: false
4+
use ExUnit.Case
55

66
setup_all do
7-
[my_context: :setup_all]
7+
[context: :setup_all]
88
end
99

1010
setup do
11-
[first_setup: true]
11+
[initial_setup: true]
12+
end
13+
14+
setup context do
15+
assert context[:initial_setup]
16+
assert context[:context] == :setup_all
17+
[context: :setup]
1218
end
1319

1420
setup context do
@@ -17,32 +23,30 @@ defmodule ExUnit.CallbacksTest do
1723
else
1824
Process.put(:ex_unit_callback, context[:test])
1925
end
26+
[]
27+
end
2028

21-
assert context[:first_setup]
22-
assert context[:my_context] == :setup_all
23-
24-
[my_context: :setup]
29+
teardown context do
30+
assert context[:context] == :setup
31+
[]
2532
end
2633

2734
teardown context do
28-
assert context[:my_context] == :setup
2935
assert Process.get(:ex_unit_callback) == context[:test]
3036
Process.delete(:ex_unit_callback)
3137
[]
3238
end
3339

3440
teardown_all context do
35-
assert context[:my_context] == :setup_all
41+
assert context[:context] == :setup_all
3642
[]
3743
end
3844

39-
test :callback, context do
40-
assert context[:my_context] == :setup
41-
assert Process.get(:ex_unit_callback) == :test_callback
45+
test "callbacks can run custom code" do
46+
assert Process.get(:ex_unit_callback) == :"test callbacks can run custom code"
4247
end
4348

44-
# Ensure we run at least two tests, so setup is run twice
45-
test :ok do
46-
assert :ok == :ok
49+
test "receives context from callback", context do
50+
assert context[:context] == :setup
4751
end
4852
end
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Code.require_file "../../test_helper.exs", __FILE__
2+
3+
defmodule ExUnit.SampleCase do
4+
use ExUnit.CaseTemplate
5+
6+
using _ do
7+
quote do
8+
import unquote(__MODULE__)
9+
end
10+
end
11+
12+
def hello do
13+
"world"
14+
end
15+
16+
setup_all do
17+
[context: :setup_all]
18+
end
19+
20+
setup context do
21+
assert context[:context] == :setup_all
22+
[context: :setup]
23+
end
24+
25+
teardown context do
26+
assert context[:context] == :setup
27+
[]
28+
end
29+
30+
teardown_all context do
31+
assert context[:context] == :setup_all
32+
[]
33+
end
34+
end
35+
36+
defmodule ExUnit.CaseTemplateTest do
37+
use ExUnit.SampleCase, async: true
38+
39+
test "receives context from parent case", context do
40+
assert context[:context] == :setup
41+
end
42+
43+
test "using code is executed" do
44+
assert hello == "world"
45+
end
46+
end

0 commit comments

Comments
 (0)