hauleth

hauleth

How do I write Elixir tests?

I wanted to write down some of the main guides I use when writing Elixir tests, so I have summarised them in blogpost.

Furthermore, I think that mocking must be destroyed.

Most Liked

krasenyp

krasenyp

Man, don’t get me started on mocking. I’ve been looking at poor designs for years and the only way to test anything at all is by using mocks. You talk about DI in relation to mocking, which is great, but I also miss programming against a contract which can be achieved through protocols.

tfwright

tfwright

Agree with describe with function name but I also include the specific input data case (with foo and bar) because I prefer to test those in isolation to avoid cascading failures and ballooning setup.

I have tried strictly using contexts for setup and just find it too cumbersome, although I grant it seems more pure and I also avoid libraries for struct generation that “compete” with contexts. I write whatever code is most convenient for the case. Sometimes that’s a context function, mostly it’s a private helper calling insert!. Have yet to be bitten by this.

I am on team “don’t write application code for the sake of tests” so I just mock stuff. It’s a tradeoff, but I’d rather my code running in prod as as univocal and expressive as possible, even if that costs some cycles. But that’s from writing code for 25 years before AI came along, I find some of my personal “guidelines” bending quite a bit recently.

fuelen

fuelen

I would ask the opposite question. Do you want a test suite that checks your code in states the system can actually reach in production, paying for it with some slower setup, or do you want fast tests that run on data no business flow could actually produce? “Just write a valid setup by hand” is easy to say. So is “just write bug free code”. If either was realistic, we would not need tests in the first place :slight_smile:

Context functions carry a contract, and for anything non trivial that contract is much simpler than putting rows into tables by hand. Let’s say you write a chat and you want a thread with a first message. The function is something like start_thread(sender, recipient, initial_message). Internally it creates the thread, links both users, inserts the message, marks it read for sender and unread for recipient. One line in your test setup vs. a lot of ceremony to recreate that exact state directly in the DB.

So is it really “more complex”? I’d say writing it is less complex. There is more code running, yes. But that code is exactly what reduces the space of valid fixture states down to what the system can actually produce. To me that is the whole point, not a cost.

Heavy, yes, in absolute terms. But ex_machina is not free either, and I think people underestimate this. :org has one :user and has many :project, and :project has :created_by and :approved_by assocs, both pointing at :user. Declare those naively as associations and a plain insert(:project) will create three users where you wanted one. To prevent that you start writing factory logic that walks through the other associations to reuse an existing user, and the simple user: build(:user) one liner turns into something messy inside the factory file. The complexity does not disappear.

My personal take: have a centralized test setup layer built on functions, and by default let it call context functions. When a specific context function is too heavy and is used in most tests (for example, users are created as :pending and must be activated through a separate flow, which is correct for the business but annoying as a prerequisite for every test), I add a lighter setup helper for that case, with a short comment explaining why it skips the normal flow.

Last Post!

johns10davenport

johns10davenport

Mocks are brittle if calling an external service. Less so if you’re calling your own functions, but then you can just call your own functions. Definitely my least used test artifact at this point.

DI is preferable. I have an in memory file system fake I inject into an environment abstraction in my main app. It’s fine.

I really prefer to use the cassette approach at the boundary though. It’s the most reliable and least brittle paradigm imo.

Where Next?

Popular in Blog Posts Top

pillaiindu
Our very handsome and humble José Valim (@josevalim) just published Kubernetes and the Erlang VM: orchestration on the large and the sma...
New
wmnnd
Here’s the story how one of the world’s first production deployments of LiveView came to be - and how trying to improve it almost caused ...
New
brainlid
This post asks if we can remove Alpine from the PETAL stack. Can we do everything we need with just LiveView? Also, let’s explore an area...
New
brainlid
Building a LiveView powered chat app is easier than ever when using Streams! Sophie DeBenedetto shows us how in this article. She createa...
New
ryotsu
How does a torrent client download huge files from the internet with just a .torrent file? In this post we’ll explore the BitTorrent prot...
New
danschultzer
How to set up the Content Security Policy header with Phoenix LiveView and support inline styles and scripts with CSP nonces.
New
marcin
Hi! :waving_hand: I wanted to refresh my knowledge on how to mix phx.gen.auth with local password users db, as well as OAuth providers s...
New

Other popular topics Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36689 110
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New

We're in Beta

About us Mission Statement