Skip to content

Commit cb5806b

Browse files
author
José Valim
committed
Deprecate teardown and teardown_all
1 parent db70fd5 commit cb5806b

File tree

8 files changed

+106
-94
lines changed

8 files changed

+106
-94
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* [Kernel] Ensure Mix `_build` structure works on Windows when copying projects
1616

1717
* Soft deprecations (no warnings emitted)
18+
* [ExUnit] `teardown/2` and `teardown_all/2` are deprecated in favor of `on_exit/1` callbacks
1819

1920
* Deprecations
2021
* [Access] `Access.access/2` is deprecated in favor of `Access.get/2`

lib/elixir/test/elixir/file_test.exs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ defmodule Elixir.FileCase do
1212

1313
setup do
1414
File.mkdir_p!(tmp_path)
15-
:ok
16-
end
17-
18-
teardown do
19-
File.rm_rf(tmp_path)
15+
on_exit(fn -> File.rm_rf(tmp_path) end)
2016
:ok
2117
end
2218
end

lib/ex_unit/lib/ex_unit/callbacks.ex

Lines changed: 70 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,64 +2,73 @@ defmodule ExUnit.Callbacks do
22
@moduledoc ~S"""
33
Defines ExUnit Callbacks.
44
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.
77
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.
1112
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.
1516
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.
1922
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.
2133
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.
2537
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.
2839
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.
3442
3543
## Examples
3644
3745
defmodule AssertionTest do
3846
use ExUnit.Case, async: true
3947
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+
4056
# `setup` is called before each test is run
4157
setup do
4258
IO.puts "This is a setup callback"
4359
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
4565
{:ok, hello: "world"}
4666
end
4767
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
4970
setup context do
50-
# We can access the current test in the context
5171
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"
6372
:ok
6473
end
6574
@@ -96,7 +105,7 @@ defmodule ExUnit.Callbacks do
96105
end
97106

98107
@doc """
99-
Called before the start of each test.
108+
Defines a callback to be run before each test in a case.
100109
"""
101110
defmacro setup(var \\ quote(do: _), block) do
102111
quote bind_quoted: [var: escape(var), block: escape(block)] do
@@ -107,42 +116,49 @@ defmodule ExUnit.Callbacks do
107116
end
108117

109118
@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.
114120
"""
115-
defmacro teardown(var \\ quote(do: _), block) do
121+
defmacro setup_all(var \\ quote(do: _), block) do
116122
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)}"
118124
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]
120126
end
121127
end
122128

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__))
128133
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)}"
130135
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]
132137
end
133138
end
134139

135-
@doc """
136-
Called once after the last test finishes without emitting an `:exit` message.
137-
"""
140+
@doc false
138141
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__))
139144
quote bind_quoted: [var: escape(var), block: escape(block)] do
140145
name = :"__ex_unit_teardown_all_#{length(@ex_unit_teardown_all)}"
141146
defp unquote(name)(unquote(var)), unquote(block)
142147
@ex_unit_teardown_all [name|@ex_unit_teardown_all]
143148
end
144149
end
145150

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+
"""
146162
@spec on_exit(term, (() -> term)) :: :ok
147163
def on_exit(ref \\ make_ref, callback) do
148164
case ExUnit.OnExitHandler.add(self, ref, callback) do

lib/ex_unit/test/ex_unit_test.exs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ defmodule ExUnitTest do
55

66
setup do
77
ExUnit.configure(formatters: [])
8-
:ok
9-
end
10-
11-
teardown do
12-
ExUnit.configure(formatters: [ExUnit.CLIFormatter])
8+
on_exit(fn ->
9+
ExUnit.configure(formatters: [ExUnit.CLIFormatter])
10+
end)
1311
:ok
1412
end
1513

lib/mix/test/mix/shell_test.exs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ defmodule Mix.ShellTest do
1111
ExUnit.CaptureIO.capture_io(from, somefunc) |> String.replace("\r\n","\n")
1212
end
1313

14+
setup do
15+
on_exit fn ->
16+
Mix.shell(Mix.Shell.Process)
17+
end
18+
:ok
19+
end
20+
1421
test "shell process" do
1522
Mix.shell.info "abc"
1623
Mix.shell.error "def"
@@ -57,9 +64,4 @@ defmodule Mix.ShellTest do
5764
assert Mix.shell.cmd("echo first && echo second") == 0
5865
end) |> String.replace(" \n", "\n")) == "first\nsecond\n"
5966
end
60-
61-
teardown do
62-
Mix.shell(Mix.Shell.Process)
63-
:ok
64-
end
6567
end

lib/mix/test/mix/tasks/app.start_test.exs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,16 @@ defmodule Mix.Tasks.App.StartTest do
2222
end
2323

2424
setup config do
25-
if config[:app] do
25+
if app = config[:app] do
2626
:error_logger.tty(false)
27-
end
28-
:ok
29-
end
3027

31-
teardown config do
32-
if app = config[:app] do
33-
:application.stop(app)
34-
:application.unload(app)
28+
on_exit fn ->
29+
:application.stop(app)
30+
:application.unload(app)
31+
:error_logger.tty(true)
32+
end
3533
end
36-
:ok
37-
end
3834

39-
teardown do
40-
:error_logger.tty(true)
4135
:ok
4236
end
4337

lib/mix/test/mix/tasks/loadconfig_test.exs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ defmodule Mix.Tasks.LoadconfigTest do
55

66
@apps [:my_app, :other_app]
77

8-
teardown do
9-
Enum.each @apps, fn app ->
10-
Enum.each Application.get_all_env(app), fn {key, _} ->
11-
Application.delete_env(app, key, persistent: true)
8+
setup do
9+
on_exit fn ->
10+
Enum.each @apps, fn app ->
11+
Enum.each Application.get_all_env(app), fn {key, _} ->
12+
Application.delete_env(app, key, persistent: true)
13+
end
1214
end
1315
end
1416
:ok

lib/mix/test/test_helper.exs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,17 @@ defmodule MixTest.Case do
1919
end
2020
end
2121

22-
teardown do
23-
Mix.env(:dev)
24-
Mix.Task.clear
25-
Mix.Shell.Process.flush
26-
Mix.ProjectStack.clear_cache
27-
Mix.ProjectStack.clear_stack
28-
System.put_env("MIX_HOME", tmp_path(".mix"))
29-
delete_tmp_paths
22+
setup do
23+
on_exit fn ->
24+
Mix.env(:dev)
25+
Mix.Task.clear
26+
Mix.Shell.Process.flush
27+
Mix.ProjectStack.clear_cache
28+
Mix.ProjectStack.clear_stack
29+
System.put_env("MIX_HOME", tmp_path(".mix"))
30+
delete_tmp_paths
31+
end
32+
3033
:ok
3134
end
3235

@@ -97,7 +100,7 @@ defmodule MixTest.Case do
97100
end
98101
end
99102
end
100-
103+
101104
def os_newline do
102105
case :os.type do
103106
{:win32, _} -> "\r\n"

0 commit comments

Comments
 (0)