@@ -2,64 +2,73 @@ defmodule ExUnit.Callbacks do
2
2
@ moduledoc ~S"""
3
3
Defines ExUnit Callbacks.
4
4
5
- This module defines four callbacks: `setup_all`, `teardown_all`,
6
- `setup` and `teardown` .
5
+ This module defines both `setup_all` and `setup` callbacks, as well as
6
+ the `on_exit` facility .
7
7
8
- These callbacks are defined via macros and each one can optionally receive
9
- a map with metadata, usually referred to as `context`. The callback
10
- may optionally put extra data into `context` to be used in the tests.
8
+ The setup callbacks are defined via macros and each one can optionally
9
+ receive a map with metadata, usually referred to as `context`. The
10
+ callback may optionally put extra data into `context` to be used in
11
+ the tests.
11
12
12
- If you return `{:ok, <dict>}` from `setup` or `teardown`, the keyword
13
- list will be merged into the context that will be available in all
14
- subsequent `setup`, `test` or `teardown` calls .
13
+ The `setup_all` callbacks are invoked once before the first test's `setup`
14
+ and all `setup` callbacks are run before each test. No callback runs if the
15
+ test case has no tests or all tests were filtered out .
15
16
16
- Similarly, returning `{:ok, <dict>}` from `setup_all` or
17
- `teardown_all` will merge the keyword list into the context that will be
18
- available in all subsequent `setup_all` or `teardown_all` calls.
17
+ `on_exit` callbacks are registered on demand, usually to undo an action
18
+ performed by a setup callback. `on_exit` may also take a reference,
19
+ allowing callback to be overridden in the future. A registered `on_exit`
20
+ callback always runs, while failures in `setup` and `setup_all` will stop
21
+ all remaining setup callbacks from executing.
19
22
20
- Returning `:ok` leaves the context unchanged in both cases.
23
+ Finally, `setup_all` callbacks run in the test case process, while all
24
+ `setup` callbacks run in the same process as the test itself. `on_exit`
25
+ callbacks always run in a separate process than the test case or the
26
+ test itself.
27
+
28
+ ## Context
29
+
30
+ If you return `{:ok, <dict>}` from `setup_all`, the dictionary
31
+ will be merged into the current context and be available in all
32
+ subsequent `setup_all`, `setup` and the test itself.
21
33
22
- Returning anything else from `setup` or `teardown` will force the current
23
- test to fail, and subsequent `setup`, `test` and `teardown` callbacks won't
24
- be called for it .
34
+ Similarly, returning `{:ok, <dict>}` from `setup`, the dict returned
35
+ will be merged into the current context and be available in all
36
+ subsequent `setup` and the `test` itself .
25
37
26
- Returning anything else from `setup_all` or `teardown_all` will force the
27
- whole case to fail, and no other callback will be called.
38
+ Returning `:ok` leaves the context unchanged in both cases.
28
39
29
- It is possible to define multiple `setup` and `teardown` callbacks and they will
30
- be called sequentially. In the case of `setup_all` and `teardown_all` callbacks,
31
- each `setup_all` will be called only once before the first test's `setup` and each
32
- `teardown_all` will be called once after the last test. No callback runs if the
33
- test case has no tests or all tests were filtered out via `include`/`exclude`.
40
+ Returning anything else from `setup_all` will force all tests to fail,
41
+ while a bad response from `setup` causes the current test to fail.
34
42
35
43
## Examples
36
44
37
45
defmodule AssertionTest do
38
46
use ExUnit.Case, async: true
39
47
48
+ # `setup_all` is called once before every test
49
+ setup_all do
50
+ IO.puts "Starting AssertionTest"
51
+
52
+ # No metadata
53
+ :ok
54
+ end
55
+
40
56
# `setup` is called before each test is run
41
57
setup do
42
58
IO.puts "This is a setup callback"
43
59
44
- # Return extra metadata, it must be a keyword list / map
60
+ on_exit fn ->
61
+ IO.puts "This is invoked once the test is done"
62
+ end
63
+
64
+ # Returns extra metadata, it must be a dict
45
65
{:ok, hello: "world"}
46
66
end
47
67
48
- # Same as `setup`, but receives the context for the current test
68
+ # Same as `setup`, but receives the context
69
+ # for the current test
49
70
setup context do
50
- # We can access the current test in the context
51
71
IO.puts "Setting up: #{context[:test]}"
52
-
53
- # We can also access the data returned from `setup/0`
54
- assert context[:hello] == "world"
55
-
56
- # No metadata
57
- :ok
58
- end
59
-
60
- # This is called after each test finishes
61
- teardown context do
62
- assert context[:hello] == "world"
63
72
:ok
64
73
end
65
74
@@ -96,7 +105,7 @@ defmodule ExUnit.Callbacks do
96
105
end
97
106
98
107
@ doc """
99
- Called before the start of each test.
108
+ Defines a callback to be run before each test in a case .
100
109
"""
101
110
defmacro setup ( var \\ quote ( do: _ ) , block ) do
102
111
quote bind_quoted: [ var: escape ( var ) , block: escape ( block ) ] do
@@ -107,42 +116,49 @@ defmodule ExUnit.Callbacks do
107
116
end
108
117
109
118
@ doc """
110
- Called after the completion of each test.
111
-
112
- Note that if the test crashed with an `:exit`
113
- message, `teardown` will not be run.
119
+ Defines a callback to be run before all tests in a case.
114
120
"""
115
- defmacro teardown ( var \\ quote ( do: _ ) , block ) do
121
+ defmacro setup_all ( var \\ quote ( do: _ ) , block ) do
116
122
quote bind_quoted: [ var: escape ( var ) , block: escape ( block ) ] do
117
- name = :"__ex_unit_teardown_ #{ length ( @ ex_unit_teardown ) } "
123
+ name = :"__ex_unit_setup_all_ #{ length ( @ ex_unit_setup_all ) } "
118
124
defp unquote ( name ) ( unquote ( var ) ) , unquote ( block )
119
- @ ex_unit_teardown [ name | @ ex_unit_teardown ]
125
+ @ ex_unit_setup_all [ name | @ ex_unit_setup_all ]
120
126
end
121
127
end
122
128
123
- @ doc """
124
- Called before the start of a case, i.e. called once before the first test in
125
- the current module and before any `setup` callbacks.
126
- """
127
- defmacro setup_all ( var \\ quote ( do: _ ) , block ) do
129
+ @ doc false
130
+ defmacro teardown ( var \\ quote ( do: _ ) , block ) do
131
+ # IO.write :stderr, "teardown in ExUnit is deprecated, please use on_exit/1 instead\n" <>
132
+ # Exception.format_stacktrace(Macro.Env.stacktrace(__CALLER__))
128
133
quote bind_quoted: [ var: escape ( var ) , block: escape ( block ) ] do
129
- name = :"__ex_unit_setup_all_ #{ length ( @ ex_unit_setup_all ) } "
134
+ name = :"__ex_unit_teardown_ #{ length ( @ ex_unit_teardown ) } "
130
135
defp unquote ( name ) ( unquote ( var ) ) , unquote ( block )
131
- @ ex_unit_setup_all [ name | @ ex_unit_setup_all ]
136
+ @ ex_unit_teardown [ name | @ ex_unit_teardown ]
132
137
end
133
138
end
134
139
135
- @ doc """
136
- Called once after the last test finishes without emitting an `:exit` message.
137
- """
140
+ @ doc false
138
141
defmacro teardown_all ( var \\ quote ( do: _ ) , block ) do
142
+ # IO.write :stderr, "teardown_all in ExUnit is deprecated, please use on_exit/1 instead\n" <>
143
+ # Exception.format_stacktrace(Macro.Env.stacktrace(__CALLER__))
139
144
quote bind_quoted: [ var: escape ( var ) , block: escape ( block ) ] do
140
145
name = :"__ex_unit_teardown_all_#{ length ( @ ex_unit_teardown_all ) } "
141
146
defp unquote ( name ) ( unquote ( var ) ) , unquote ( block )
142
147
@ ex_unit_teardown_all [ name | @ ex_unit_teardown_all ]
143
148
end
144
149
end
145
150
151
+ @ doc """
152
+ Defines a callback that runs on the test (or test case) exit.
153
+
154
+ An `on_exit` callback is a function that receives no arguments and
155
+ runs in a separate process than the caller.
156
+
157
+ `on_exit/2` is usually called from `setup` and `setup_all` callbacks,
158
+ often to undo the action performed during `setup`. However, `on_exit`
159
+ may also be called dynamically, where a reference can be used to
160
+ guarantee the callback will be invoked only once.
161
+ """
146
162
@ spec on_exit ( term , ( ( ) -> term ) ) :: :ok
147
163
def on_exit ( ref \\ make_ref , callback ) do
148
164
case ExUnit.OnExitHandler . add ( self , ref , callback ) do
0 commit comments