hauleth
Split Thread: Fixtures vs Factories in Elixir
Use factories and not fixtures
Gods, I cannot even express how much I disagree with it.
Most Liked
josevalim
I will add some cents to the discussion that I haven’t seen mentioned yet. Below, I will be talking about database-backed fixtures (and not necessarily fixtures as a whole):
-
One of the downsides of fixtures is that it is shared data across all of your tests. So sometimes you will change your fixtures, because you need new data to be used in some new tests, and other tests may now fail. This gets worse if projects define a large amount of fixture data. The correct approach, as mentioned earlier, is to define fixtures for a basic feature set that will be shared across all tests.
-
The particular fixtures implementation in Rails caused some issues because referential integrity and data validations are disabled or not really used in Rails, so you could easily end-up with invalid data or data that would never exist in the database through the regular application workload.
-
It is actually super straight-forward to have fixtures in Ecto: just write to the database in your test_helper.exs before you start the SQL sandbox.
Finally, a summary that was given to me a long time ago that explains when to use fixtures vs factories well is:
- Use fixtures for the setup data (i.e. the data you need to define before you can start writing your test)
- Use factories for the data under test, especially because you want the data being tested close to the test itself
In my opinion, factories are actually easier to get started as they require less discipline (with bigger costs in the long term), while fixtures are really easy to mess up at the beginning (but pay off if well-structured).
BartOtten
Please try; otherwise your comment is nothing but negative energy.
TheFirstAvenger
My .02 on fixtures vs factories:
My problem with Fixtures is that it buries the logic that you are testing. Repeatedly in my career I have jumped in to work on a fixture-using project, and without fail I end up wasting way too much time on what should be a simple task of fixing or updating a test, because the logic of what exactly is being tested was not clear in the test, it was buried implicitly in the fixture data.
For example, if you were to come across this test which is all of a sudden failing, which relies on fixtures, what would you do to fix it?
test “returns confrazzled users” do
assert [%{id: 1}, %{id: 3}] = Confrazzler.get_confrazzled_users()
end
From the test, it is unclear what is being tested, or why it is failing. You could look in the fixtures, but even then it most likely wouldn’t be clear what exactly is supposed to be testing. You would have to dig into the actual code to figure out what get_confrazzled_users actually does. In a recent project, the function being tested used a 100 line SQL statement. It was basically impossible to reverse engineer what exactly was being tested.
Now, if it were written with factories, you would instead see this code:
test “returns confrazzled users“ do
%{id: id1} = insert(:user, frazzled: “con”)
%{id: id2} = insert(:user, frazzled: “pro”)
%{id: id3} = insert(:user, frazzled: “con”)
assert [%{id: ida}, %{id: idb}] = Confrazzler.get_confrazzled_users()
assert Enum.sort([ida, idb]) == Enum.sort([id1, id2])
end
In the factory test, the setup is clear and explicit, and part of the test. There is no digging in fixtures to figure out what the implicit assumptions are, or combing through 20 fields to try and figure out which field(s) are actually being tested in the current test, because the answer to that is clear in the setup inside the test. It is easy to understand that this test is testing that get_confrazzled_users returns users where their frazzled field is “con”.
In the fixture test, frazzled: “con” was set for id 1 and 3 in the fixture. That fact was known by the person who wrote the test initially, but it is hidden knowledge to everyone else reading it, and hidden to anyone who subsequently needs to update the fixtures.
Beyond the issue of fixtures burying logic, there have been plenty of issues I have caught because the factory was using randomly generated data and triggered an edge case I didn’t think of, which the static fixture never would have covered.
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









