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.

View other posts tagged: engineering elixir 28 days of elixir