28 days of Elixir - Silly Mistakes I Make Again and Again
It’s day 2 of my 28 days of Elixir blogging. I have 5 more ideas in the funnel, and I fear that coming up with 28 interesting topics will be hard. I’ll definitely be leaning on co-workers for ideas, but don’t hesitate to reach out if you have any interesting ideas that you’d want me to explore!
I’ve been writing Elixir daily at SalesLoft, and have gotten pretty quick with development and testing of my apps. However, a few things just keep catching me up every time:
Charlist and Strings
In both Ruby and Javascript, it is acceptable to swap ‘ and “ for strings, in many cases. This means that 'a’ === "a” in Javascript. However, ‘a’ != “a” in Elixir. This is due to charlist and string being implemented differently. Charlist is often used for interfacing with erlang directly, because erlang does not have the concept of “strings”.
One mistake I will often catch is swapping the usage of the quote constructs and then doing equality or other comparisons, and being bewildered when the strings do not match. Catching this error can be a bit tricky if the error message doesn’t show you that the types are different, but a usual symptom is being completely bewildered why two matching strings are not matching. The most significant place to pay attention to is when interfacing with code between libraries, as the target and source are separated.
Running mix test on the source file
This one is always a face-palmer. When running mix test test/my_test.exs
, be careful of
accidentally copying a path like mix test lib/my.ex
. The symptom of this mistake is a
passing spec suite which runs 0 specs, and complains about a re-defined module.
Define a test file with the same name as the real module, rather than test
My flow for creating a module is to create the source module (.ex), then running
(opt-g-t) to create a test script (.exs). I will then copy the source module definition
and add ExUnit and Test
to the end of the module.
As I’m going red/green on my code, I sometimes find myself shocked that a function
doesn’t exist when I most certainly just defined it. After looking around wondering
what is going on, I realize I forgot to include Test
at the end of the module. This
re-initializes the module (without real code) but is still a sementically valid
test file.
Not recognizing pattern matching in test helpers such as assert received
This is one of the more interesting / subtle mistakes that I find myself making. I especially see this in older Elixir code that I wrote where I didn’t fully understand what was going on.
I will be doing a task like writing / testing a websocket channel and find that my code passes perfectly when I know that it’s definitely not working correctly. I will be trying to make my code go red but simply can’t make it. Usually, it turns out that I’m using a function like assert_push/3 that is actually a macro. The way these are written, the function is actually doing pattern matching rather than exact checks. For instance:
expected_payload = %{foo: "bar"}
assert_push "some_event", expected_payload
is different than:
expected_payload = %{foo: "bar"}
assert_push "some_event", ^expected_payload
This is highlighted in the assert_push documentation, but essentially the first example doesn’t check that the payload is exactly the expected payload. This means that there could be something like a data leak and our test wouldn’t catch it! Pattern matching the responses can be super useful, but make sure that it’s happening when you expect it, and not when you don’t!
Thanks for reading, today. Keep up through the month of February to see if I can stand subjecting myself to 28 days of straight writing.