Skip to content

Commit 4087560

Browse files
authored
Support ellipsis in doctest exceptions (#14233)
1 parent 1de463f commit 4087560

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

lib/ex_unit/lib/ex_unit/doc_test.ex

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defmodule ExUnit.DocTest do
2-
@moduledoc """
2+
@moduledoc ~S"""
33
Extract test cases from the documentation.
44
55
Doctests allow us to generate tests from code examples found
@@ -131,7 +131,7 @@ defmodule ExUnit.DocTest do
131131
132132
You can also showcase expressions raising an exception, for example:
133133
134-
iex(1)> raise "some error"
134+
iex> raise "some error"
135135
** (RuntimeError) some error
136136
137137
Doctest will look for a line starting with `** (` and it will parse it
@@ -141,6 +141,19 @@ defmodule ExUnit.DocTest do
141141
Therefore, it is possible to match on multiline messages as long as there
142142
are no empty lines on the message itself.
143143
144+
Asserting on the full exception message might not be possible because it is
145+
non-deterministic, or it might result in brittle tests if the exact message
146+
changes and gets more detailed.
147+
Since Elixir 1.19.0, doctests allow the use of an ellipsis (`...`) at the
148+
end of messages:
149+
150+
iex> raise "some error in pid: #{inspect(self())}"
151+
** (RuntimeError) some error in pid: ...
152+
153+
iex> raise "some error in pid:\n#{inspect(self())}"
154+
** (RuntimeError) some error in pid:
155+
...
156+
144157
## When not to use doctest
145158
146159
In general, doctests are not recommended when your code examples contain
@@ -565,7 +578,7 @@ defmodule ExUnit.DocTest do
565578
"Doctest failed: expected exception #{inspect(exception)} but got " <>
566579
"#{inspect(actual_exception)} with message #{inspect(actual_message)}"
567580

568-
actual_message != message ->
581+
not error_message_matches?(actual_message, message) ->
569582
"Doctest failed: wrong message for #{inspect(actual_exception)}\n" <>
570583
"expected:\n" <>
571584
" #{inspect(message)}\n" <>
@@ -588,6 +601,17 @@ defmodule ExUnit.DocTest do
588601
end
589602
end
590603

604+
defp error_message_matches?(actual, expected) when actual == expected, do: true
605+
606+
defp error_message_matches?(actual, expected) do
607+
if String.ends_with?(expected, "...") do
608+
ellipsis_removed = binary_slice(expected, 0..-4//1)
609+
String.starts_with?(actual, ellipsis_removed)
610+
else
611+
false
612+
end
613+
end
614+
591615
defp test_import(_mod, false), do: []
592616
defp test_import(mod, _), do: [quote(do: import(unquote(mod)))]
593617

lib/ex_unit/test/ex_unit/doc_test_test.exs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,34 @@ defmodule ExUnit.DocTestTest.PatternMatching do
548548
end
549549
|> ExUnit.BeamHelpers.write_beam()
550550

551+
defmodule ExUnit.DocTestTest.Ellipsis do
552+
@doc """
553+
iex> ExUnit.DocTestTest.Ellipsis.same_line_err(self())
554+
** (ArgumentError) Unexpected: ...
555+
"""
556+
def same_line_err(arg) do
557+
raise ArgumentError, "Unexpected: #{inspect(arg)}"
558+
end
559+
560+
@doc """
561+
iex> ExUnit.DocTestTest.Ellipsis.multi_line_err(self())
562+
** (ArgumentError) Unexpected:
563+
...
564+
"""
565+
def multi_line_err(arg) do
566+
raise ArgumentError, "Unexpected:\n#{inspect(arg)}"
567+
end
568+
569+
@doc """
570+
iex> ExUnit.DocTestTest.Ellipsis.triple_dot_err(:foo)
571+
** (ArgumentError) :foo ...
572+
"""
573+
def triple_dot_err(arg) do
574+
raise ArgumentError, "#{inspect(arg)} ..."
575+
end
576+
end
577+
|> ExUnit.BeamHelpers.write_beam()
578+
551579
defmodule ExUnit.DocTestTest do
552580
use ExUnit.Case
553581

@@ -574,6 +602,8 @@ defmodule ExUnit.DocTestTest do
574602
doctest ExUnit.DocTestTest.HaikuIndent4UsingInspectOpts,
575603
inspect_opts: [custom_options: [indent: 4]]
576604

605+
doctest ExUnit.DocTestTest.Ellipsis
606+
577607
import ExUnit.CaptureIO
578608

579609
test "multiple functions filtered with :only" do

0 commit comments

Comments
 (0)