pinksynth

pinksynth

Tip: Using Ecto transactions in tests

Whenever tests have to interact with an Ecto.Repo, sometimes it’s necessary to test a few different branches or paths that data can take. This can be tedious with a database and may result in “resetting” records so they may be re-tested. Here’s a crude example of “resetting” some data to continue with a test:

test "vaccinate_dog/1 vaccinates dog and creates vax record with the corresponding name" do
  dog = dog_fixture()
  
  # Perform assertions about vaccinations
  assert :ok = vaccinate_dog(dog, "bordetella")
  assert %{id: dog_id, vaccinated: true} = dog = Repo.one!(Dog)
  assert %{dog_id: ^dog_id, type: "bordetella"} = Repo.one!(Vax)

  # Reset state for the dog
  Repo.delete_all(Vax)
  dog |> Ecto.Changeset.change(%{vaccinated: false}) |> Repo.update!()

  # Performmore  assertions about vaccinations
  assert :ok = vaccinate_dog(dog, "rabies")
  assert %{id: dog_id, vaccinated: true} = dog = Repo.one!(Dog)
  assert %{dog_id: ^dog_id, type: "rabies"} = Repo.one!(Vax)
end

By using Repo.transaction/2, it’s easy to simply roll back a transaction rather than “resetting” anything (assuming that side-effects are not a concern for the purposes of the test.

test "vaccinate_dog/1 vaccinates dog and creates vax record with the corresponding name" do
  dog = dog_fixture()

  for vaccine <- ["rabies", "bordetella"] do
    in_transaction(fn ->
      assert :ok = vaccinate_dog(dog, vaccine)
      assert %{id: dog_id, vaccinated: true} = dog = Repo.one!(Dog)
      assert %{dog_id: ^dog_id, type: vaccine} = Repo.one!(Vax)
    end)
  end
  
  defp in_transaction(fun) when is_function(fun, 0) do
    Repo.transaction(fn ->
      fun.()
      Repo.rollback(:ok)
    end)
  end
end

I know that it’s generally best to decouple the DB and the business logic as much as possible, but I have found this to be a useful way to succinctly cover more ground within Repo-related tests.

Most Liked

al2o3cr

al2o3cr

This is usually where I’d stop this test and write another one, TBH. Unless dog_fixture is very expensive to compute, I’d prefer to see duplication if testing with "rabies" vs "bordetella" exposes different behavior.

Also note there’s no support AFAIK for nested transactions, so this won’t work as expected if used with the sandbox-enabled test harness that’s generated by Phoenix.

Where Next?

Popular in Guides/Tuts Top

wfgilman
I’m writing up this quick “How to” because what I thought was going to be an easy implementation of a Plug to validate a webhook request ...
New
tfwright
I thought I’d share a small project I’m working on to gain some familiarty with LiveView in a Phoenix app. Github Repo Deployment It’s...
New
Logan
Here is an example of a Mix application that utilizes Cowboy to handle websocket connections. If anyone has an idea about making this wor...
New
fuelen
Hi all! Just want to share a small code snippet which allows writing CASE expressions using macro which is similar to cond. Here is an ...
New
jswny
Hello everyone, I recently redesigned my entire deployment process for Phoenix apps based on Docker. I really like the strategy that I ca...
New
fmcgeough
pipe into case? I use that fairly frequently…unless I’m misunderstanding what you’re wanting…could be.. its still very early… str = "Hel...
New
egze
I was preparing to deploy a production application to AWS Fargate, and to practice I wanted to play with DNS polling and node discovery o...
New
alejandroErik
POST IN CONSTRUCTION Process for compile erlang otp 20 with odbc-unix for Oracle connections on Solaris 11.3 for 64 bits: Introductio...
New
nelsonic
Complete beginners Todo List Tutorial in Phoenix 1.5.3 (latest and greatest): https://github.com/dwyl/phoenix-todo-list-tutorial It’s a...
New
KoviRobi
Hi, I’ve written the following to debug function calls, not sure if it’s useful for anyone else, and if so should I put it somewhere? i...
New

Other popular topics Top

TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41539 114
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 126479 1222
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New

We're in Beta

About us Mission Statement