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.

Last Post!

pinksynth

pinksynth

Yeah, admittedly this is not the best example. My actual use case is a module which does some pretty complex query composition using “filters” that can be manipulated recursively by the end user and is generally in the “hard to test” world of high-CC code. I don’t recall the specifics of how our Repo is set up in tests but we use transactions like this quite a bit and they all seem to work fine :grimacing:

Where Next?

Popular in Guides/Tuts Top

bentanweihao
I wrote a thing: http://engineering.pivotal.io/post/how-to-set-up-an-elixir-cluster-on-amazon-ec2/ Hope this is helpful!
New
mhanberg
Hi! I recently finished adding authentication to my Phoenix API, so I wanted to share what I learned. I haven’t created authentication f...
New
niku
I write an article Parameterized testing with ExUnit.The key concept is using ExUnit.Case.register_test/4 such as ExUnit.start() defmod...
New
bluegene
Hi guys, I’ve been on a personal journey to learn Elixir for the past two years. During this journey I’ve been using the spaced repetiti...
New
annad
I’m posting this for developers who are totally new to Phoenix like myself. This is all probably obvious to more skilled Phoenix develope...
New
crockwave
To integrate dropdown menus in a Phoenix Liveview app, you can use a combination of js, Hooks, CSS and your .leex and .ex code. You can...
New
Jskalc
Sorry if it’s a common knowledge, but it’s something I just learned and wanted to share. I’ve seen that mind-blowing demo of hot-reload ...
New

Other popular topics Top

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 131117 1222
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36820 110
New
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 55125 245
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
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 42716 114
New

We're in Beta

About us Mission Statement