问题
In testing my Phoenix app, I keep running into situations where I'm comparing lists of expected versus actual record IDs. Errors are tedious to interpret because Elixir keeps printing the integer lists as charlists, so my test output looks like:
Assertion with == failed
code: assert H.sort(Enum.map(list1, &(&1.id()))) == H.sort(Enum.map(list2, &(&1.id())))
left: 'stu'
right: 'st'
This is nudging me to rewrite my tests to avoid comparing lists of integers, which is tolerable, but it's a shame to just shrug and look for a workaround in a language like this. So I'm wondering if there's a way to tell Elixir/Mix to always print integer lists as lists, rather than as charlists/charstrings. I write Ruby-styled Elixir and I almost never make use of charlists, to me they're mostly a gotcha to work around.
Thanks to this answer I know there's a way to configure IEx to always print integer lists as lists. Is there a way to do so in Mix, or globally in Elixir itself, so mix test
will adopt that behavior?
回答1:
This is not configurable in ExUnit
, it goes down to calling Inspect.Algebra.to_doc/2 with the Inspect.Opts hardcoded to %Inspect.Opts{width: width}.
In your own code, you always might pass the second argument in a call to inspect/2, like
IO.inspect('ABC', charlists: :as_list)
#⇒ [65, 66, 67]
回答2:
The "humanizing" of charlists is perhaps one of the most confusing gotchas in Elixir. As @Aleksei points out, this isn't configurable in ExUnit
(although you can configure iex
by adding IEx.configure(inspect: [charlists: :as_lists])
in your .iex.exs
file).
However, you should not need to rewrite your tests. Keep in mind that the way that values are printed is only a view on the data. It does not affect how the data are stored.
Your example assertion failed because 'stu'
is not equal to 'st'
just as [115, 116, 117]
is not equal to [115, 116]
.
Look at the following code:
iex> [115, 116, 117] === 'stu'
true
The left and right sides are 100% exactly equivalent. The single-quotes merely allow humans to enter data more easily.
In your particular case, you might want to take a look at MapSet, especially if your list of IDs are unique. Then you can avoid the awkward/brittle sorting prior to comparison. With MapSet
, you can just compare the two sets for equality. For example:
expected_ids = MapSet.new([115, 116, 117])
actual_ids = MapSet.new(some_function_output)
assert MapSet.equal?(actual_ids, expected_ids)
This might help avoid the tedious tracking down of errors caused by the charlists. Another option in your case might be to convert the integers to strings during the mapping, e.g. Enum.map(list1, fn x -> to_string(x.id) end)
来源:https://stackoverflow.com/questions/65087836/elixir-configure-a-mix-project-to-always-print-charlists-as-lists