@@ -29,15 +29,15 @@ defmodule ExUnit.CaptureIO do
29
29
30
30
Returns the binary which is the captured output.
31
31
32
- By default, `capture_io` replaces the `group_leader` (`:stdio`)
33
- for the current process. Capturing the group leader is done per
34
- process and therefore can be done concurrently.
32
+ By default, `capture_io` replaces the `Process.group_leader/0` of the current
33
+ process, which is the process used by default for all IO operations. Capturing
34
+ the group leader of the current process is safe to run concurrently, under
35
+ `async: true` tests. You may also explicitly capture the group leader of
36
+ another process, however that is not safe to do concurrently.
35
37
36
- However, the capturing of any other named device, such as `:stderr`,
37
- happens globally and persists until the function has ended. While this means
38
- it is safe to run your tests with `async: true` in many cases, captured output
39
- may include output from a different test and care must be taken when using
40
- `capture_io` with a named process asynchronously.
38
+ You may also capture any other named IO device, such as `:stderr`. This is
39
+ also safe to run concurrently but, if several tests are writting to the same
40
+ device at once, captured output may include output from a different test.
41
41
42
42
A developer can set a string as an input. The default input is an empty
43
43
string. If capturing a named device asynchronously, an input can only be given
@@ -51,15 +51,28 @@ defmodule ExUnit.CaptureIO do
51
51
52
52
## IO devices
53
53
54
- You may capture the IO from any registered IO device. The device name given
55
- must be an atom representing the name of a registered process. In addition,
56
- Elixir provides two shortcuts :
54
+ You may capture the IO of the group leader of any process, by passing a `pid`
55
+ as argument, or from any registered IO device given as an `atom`. Here are
56
+ some example values :
57
57
58
- * `:stdio` - a shortcut for `:standard_io`, which maps to
59
- the current `Process.group_leader/0` in Erlang
58
+ * `:stdio`, `:standard_io` - a shortcut for capturing the group leader
59
+ of the current process. It is equivalent to passing `self()` as the
60
+ first argument. This is safe to run concurrently and captures only
61
+ the of the current process or any child process spawned inside the
62
+ given function
60
63
61
- * `:stderr` - a shortcut for the named process `:standard_error`
62
- provided in Erlang
64
+ * `:stderr`, `:standard_error` - captures all IO to standard error
65
+ (represented internally by an Erlang process named `:standard_error`).
66
+ This is safe to run concurrently but it will capture the output
67
+ of any other test writing to the same named device
68
+
69
+ * any other atom - captures all IO to the given device given by the
70
+ atom. This is safe to run concurrently but it will capture the output
71
+ of any other test writing to the same named device
72
+
73
+ * any other pid (since v1.17.0) - captures all IO to the group leader
74
+ of the given process. This option is not safe to run concurrently
75
+ if the pid is not `self()`. Tests using this value must set `async: true`
63
76
64
77
## Options
65
78
@@ -72,9 +85,6 @@ defmodule ExUnit.CaptureIO do
72
85
* `:encoding` (since v1.10.0) - encoding of the IO device. Allowed
73
86
values are `:unicode` (default) and `:latin1`.
74
87
75
- * `:pid` (since v1.17.0) - a process identifier. This option can be
76
- used to capture IO from an already-started process.
77
-
78
88
## Examples
79
89
80
90
To capture the standard io:
@@ -94,10 +104,10 @@ defmodule ExUnit.CaptureIO do
94
104
...> end) == "this is input"
95
105
true
96
106
97
- Note it is fine to use `==` with standard IO, because the content is captured
98
- per test process. However, `:stderr` is shared across all tests, so you will
99
- want to use `=~` instead of `==` for assertions on `:stderr` if your tests
100
- are async:
107
+ Note it is fine to use `==` with `:stdio` ( the default IO device), because
108
+ the content is captured per test process. However, `:stderr` is shared
109
+ across all tests, so you will want to use `=~` instead of `==` for assertions
110
+ on `:stderr` if your tests are async:
101
111
102
112
iex> capture_io(:stderr, fn -> IO.write(:stderr, "john") end) =~ "john"
103
113
true
@@ -113,6 +123,14 @@ defmodule ExUnit.CaptureIO do
113
123
Otherwise, if the standard error of any other test is captured, the test will
114
124
fail.
115
125
126
+ To capture the IO from another process, you can pass a `pid`:
127
+
128
+ capture_io(GenServer.whereis(MyServer), fn ->
129
+ GenServer.call(MyServer, :do_something)
130
+ end)
131
+
132
+ Tests that directly capture a PID cannot run concurrently.
133
+
116
134
## Returning values
117
135
118
136
As seen in the examples above, `capture_io` returns the captured output.
0 commit comments