In [Chapter 5](/guide/your-first-test/) each test built its own `Recipe.new` or created its own recipe. That works for a handful of examples. It gets tiring when ten tests all need "a recipe with a title" and you copy the same setup line every time. It also gets [flaky](/guide/kinds-of-rails-tests/#why-rails-splits-tests-at-all) when one test leaves extra records behind and the next test assumes an empty database.

So what's the solution? Enter [Fixtures](https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html?utm_source=minitestrails.com). A fixture is a named sample record you declare in YAML. Rails loads those rows into the test database before your suite runs so every test starts from the same known world. [Chapter 3](/guide/testing-tools/#fixtures) introduced the idea; this chapter makes it real by using it in your Recipes app.

<%= render Guide::ChapterProgress.new(
  feature: "Fixture data in `test/fixtures/recipes.yml` (named recipes you load with `recipes(:pancakes)`).",
  why: "Chapter 5 repeated `Recipe.new` and `Recipe.create!` in every test. Fixtures give every example the same stable starting cast so you stop copying setup and stop guessing row counts.",
  test: "Model tests: `is valid` on a fixture row.
  Integration tests: tighten `visits the list` to show fixture titles in HTML; add `shows a recipe` in `test/integration/`. Still no browser automation here.",
  later: "[Chapter 7](/guide/testing-simple-crud-system-tests/): system CRUD in `test/system/` plus integration update and destroy in `test/integration/`.
  [Chapter 8](/guide/testing-models/): integration blank-title failures in the same integration file."
) %>

## What you will do in this chapter

1. Add `test/fixtures/recipes.yml` with a few named recipes.
2. Use `recipes(:name)` in model tests instead of repeating `Recipe.create!`.
3. Grow `test/integration/recipes_integration_test.rb` with list + show backed by fixtures.
4. See how [transactional tests](https://guides.rubyonrails.org/testing.html?utm_source=minitestrails.com#transactions) don't leave any records behind in the database after each test run.
5. Commit after a green run.

## Create recipe fixtures

From your app root, open or create `test/fixtures/recipes.yml` and replace the file with the following:

```yaml
# test/fixtures/recipes.yml
pancakes:
  title: Fluffy pancakes
  description: Weekend breakfast
  prep_time: 15
  servings: 4

lentil_soup:
  title: Lentil soup
  description: Simple dinner
  prep_time: 30
  servings: 6
```

Each top-level key (`pancakes`, `lentil_soup`) is a fixture name. In tests you load them with `recipes(:pancakes)`. The name is arbitrary but should be memorable.

**Heads up:** You do not have `user_id` on recipes yet. [Chapter 11](/guide/testing-authentication/) adds users, wires recipes to owners, and stops treating everyone as a guest who can change data. Until then, the guide intentionally keeps CRUD open so you are not juggling sign-in while learning fixtures.

### Update tests that still reference `one` and `two`

The Recipe scaffold ships `test/fixtures/recipes.yml` with keys `one` and `two`. [Chapter 5](/guide/your-first-test/#what-the-scaffold-put-in-testcontrollers) showed `test/controllers/recipes_controller_test.rb` loading `recipes(:one)` in `setup`. You just replaced those YAML keys with `pancakes` and `lentil_soup`, so any test still calling the old names will break.

Run the full non-system suite now and see it for yourself:

```bash
bin/rails test
```

You will likely hit something like this before any assertion runs:

```
StandardError: No fixture named 'one' found for fixture set 'recipes'
```

Search under `test/` for `recipes(:one)` and `recipes(:two)` and point them at your new fixture names. You most probably only need to update the controller test file:

```ruby
# test/controllers/recipes_controller_test.rb
setup do
  @recipe = recipes(:pancakes)
end
```

Run the full suite again:

```bash
bin/rails test
```

You want 0 failures and 0 errors.

## Use fixtures in a model test

Chapter 5 already proved a blank title is rejected. This chapter adds a second model test: the fixture row you just declared should pass every validation on the model today.

Open `test/models/recipe_test.rb` and keep both tests:

```ruby
# test/models/recipe_test.rb
require "test_helper"

class RecipeTest < ActiveSupport::TestCase
  test "is valid" do
    assert recipes(:pancakes).valid?
  end

  test "rejects a blank title" do
    recipe = Recipe.new(title: "")
    assert_not recipe.valid?
    assert_includes recipe.errors[:title], "can't be blank"
  end
end
```

Run:

```bash
bin/rails test test/models/recipe_test.rb
```

You want 0 failures and 0 errors. If `is valid` fails, read the failure message: usually a column in the YAML does not match what the model expects (missing field, wrong type, or a validation you added later).

<%= render Shared::Tip.new(
  title: "New assertion",
  markdown: <<~MD
    **`assert recipes(:pancakes).valid?`** asks "is this true?"
    Syntax: `assert` plus a condition. You used `assert_not` in [Chapter 5](/guide/your-first-test/#example-model-test-add-the-assertions) for the opposite case. Here you prove the fixture row passes all current validations.
  MD
) %>

## Grow integration tests with fixtures

You left `test/integration/recipes_integration_test.rb` with two tests in [Chapter 5](/guide/your-first-test/#write-your-first-integration-tests) (`visits the list` and `creates a recipe`). In this chapter we will add tests for one more action `show`. Same folder, same file, still integration tests (HTTP with `get` and `post`, no browser).

### What counts as working?

| Flow | You might say |
| --- | --- |
| List | When I request the recipe list in an integration test, the response succeeds and I can see the pancake fixture title in the HTML. |
| Show | When I request one recipe by id in an integration test, the response succeeds for `recipes(:pancakes)`. |

### Add scenarios to the test file

Add commented scenario lines for the tests you are adding in `test/integration/recipes_integration_test.rb`. Leave `test "creates a recipe"` from [Chapter 5](/guide/your-first-test/#example-integration-file) as-is; fixtures do not change that scenario.

```ruby
# test/integration/recipes_integration_test.rb
require "test_helper"

class RecipesIntegrationTest < ActionDispatch::IntegrationTest
  # Actor: guest (HTTP request, no browser)
  # Starting point: recipes(:pancakes) is loaded from fixtures
  # Action: GET the recipe list
  # Expected outcome: success response; pancake title appears in the HTML
  # test "visits the list" do
  # end

  # Actor: guest (HTTP request, no browser)
  # Starting point: recipes(:pancakes) exists in fixtures
  # Action: GET the show page for that recipe
  # Expected outcome: success response
  # test "shows a recipe" do
  # end
end
```

### Update the list test

Replace the body of `test "visits the list"` to also check for the pancake recipe we added to the fixture file:

```ruby
# test/integration/recipes_integration_test.rb
test "visits the list" do
  get recipes_url
  assert_response :success
  assert_match recipes(:pancakes).title, response.body
end
```

<%= render Shared::Tip.new(
  title: "New assertion",
  markdown: <<~MD
    **`assert_match recipes(:pancakes).title, response.body`** asks "does this text appear in the response?"
    Syntax: `assert_match` plus a pattern (here the fixture title), then the string to search (`response.body` is the HTML). Proves fixture data is visible, not only that the request succeeded.
  MD
) %>

### Add show

Add a new test block for `shows a recipe` just below the `test "visits the list" do ... end` block and add the following:

```ruby
# test/integration/recipes_integration_test.rb
test "shows a recipe" do
  get recipe_url(recipes(:pancakes))
  assert_response :success
end
```

Run the integration file green before you move on:

```bash
bin/rails test test/integration/recipes_integration_test.rb
```

Your file should have three tests (`visits the list` with fixture text, `creates a recipe` from Chapter 5, `shows a recipe`). Order of these three tests inside the class doesn't matter and it is up to you on how you add them.

## Why tests do not pile up rows forever

In the integration test for `creates a recipe`, the test inserts a row during the test. While your fixtures load two recipes at the start of every example. You might be wondering: if the `create` test runs first, does the next test see three rows instead of two?

Nope, every test block starts from completely new slate. Everything is cleaned up after each test run so nothing remains in the database, this is to ensure reliability of tests by giving them clean database point. But don't trust me yet, you should verify that before moving on.

### Check the count yourself

Add this test to `test/integration/recipes_integration_test.rb` just below the `test "creates a recipe" do .. end`:

```ruby
test "only fixture rows exist at the start of each test" do
  assert_equal 2, Recipe.count
end
```

Run the full integration file (all four tests, including `creates a recipe`):

```bash
bin/rails test test/integration/recipes_integration_test.rb
```

You want 0 failures and 0 errors. If this count test passes while `creates a recipe` lives in the same file, each test still began with exactly two fixture rows. The create test may bump the count to three *during* its run, but that row does not stick around for the next test.

<%= render Shared::Tip.new(
  title: "New assertion",
  markdown: <<~MD
    **`assert_equal 2, Recipe.count`** asks "is the actual value exactly what I expected?"
    Syntax: `assert_equal` plus expected, then actual. Here you expect two fixture rows and nothing extra leaked in from another test.
  MD
) %>

You can also verify this in the rails console (after you run tests above):

1. Open console in the test environment with `bin/rails console -e test`.
2. Run `Recipe.count` and you should see "2"

You will not find leftover rows from individual tests piling up in normal runs, and that's why you will see the count of 2 there instead of 3; those 2 records are being loaded by fixtures.

### What Rails is doing

Rails wraps each test in a database transaction and rolls back when the test finishes. That is what people mean by [transactional fixtures](https://guides.rubyonrails.org/testing.html?utm_source=minitestrails.com#transactions). Your fixture rows reload to a clean state for the next test. That is why tests can create records without manually deleting them at the end, and why fixture counts stay predictable.

### Remove the sample count test

The count test was only for this exercise. Delete `test "only fixture rows exist at the start of each test"` from `test/integration/recipes_integration_test.rb`. You do not need to keep it in the suite going forward.

Run the integration file once more to confirm you are still green with your three feature tests:

```bash
bin/rails test test/integration/recipes_integration_test.rb
```

## ERB in fixtures

So far every fixture value has been plain text or a number. Sometimes you want a value that depends on today a relative time, or another bit of Ruby that would be annoying to hard-code and update by hand. Fixture YAML can run [ERB](https://docs.ruby-lang.org/en/master/ERB.html?utm_source=minitestrails.com) when Rails loads the file without needing any additional setup. You drop `<%%=` … `%>` tags in the YAML the same way you do in views.

A common case is a date that should always mean "today" when the suite runs:

```yaml
# test/fixtures/recipes.yml
dated:
  title: Recipe created <%%= Time.zone.today %>
```

You can use the same idea for other simple expressions:

```yaml
# test/fixtures/recipes.yml
expires_soon:
  title: Weeknight pasta
  prep_time: 20
  description: Added <%%= 1.week.ago.to_date %>
```

You do not need ERB for `pancakes` and `lentil_soup` recipes in this chapter. Plain YAML is enough until a column really needs to track the clock.

## Associations in fixture (covered later)

Fixtures are not only flat rows. When models `belong_to` each other, child fixture files can point at a parent by **label** (`recipe: pancakes` in `test/fixtures/ingredients.yml`). Rails wires the foreign keys when it loads the test database. You can also reach associated rows from tests (`recipes(:pancakes).ingredients`) once those child fixtures exist.

You do not have related models in the Recipes app yet, so this chapter stays with standalone recipe rows. [Chapter 9](/guide/second-resource-and-associations/#fixture-associations) adds ingredient and step fixtures and shows the full pattern. [Chapter 11](/guide/testing-authentication/) also uses the same idea when recipes `belong_to` users.

## Commit your work

Run the non-system suite and confirm green:

```bash
bin/rails test
```

You want 0 failures and 0 errors. Then:

```bash
git add .
git commit -m "Add recipe fixtures"
```

Small commits make it easier to roll back, bisect a regression, or pick up on another machine.

## What is next

You now have the same starting data in every test with the use of Fixtures. [Chapter 7](/guide/testing-simple-crud-system-tests/) uses them in system tests (`test/system/`) that click through list, show, create, update, and destroy in the browser, and finishes integration update and destroy in `test/integration/`. When you add nested models in [Chapter 9](/guide/second-resource-and-associations/#fixture-associations), the same YAML files can wire `belongs_to` rows together.

For API details on fixture files, labels, associations, and how Rails loads them, see the [ActiveRecord::FixtureSet](https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html?utm_source=minitestrails.com) docs.

Continue to **[Testing simple CRUD (system tests)](/guide/testing-simple-crud-system-tests/)**.
