Setting up the Recipe app
To make the guide practical and easy to follow, we will use an app for noting and sharing Recipes. We start from a fresh rails new, add features step by step, and grow complexity as we go. While we add features we will add tests beside the code so you always see how Minitest fits a real codebase.
Assumptions #
-
Ruby is installed.
If you do not have Ruby yet, use rbenv, asdf, mise, or another version manager you like. That makes it easy to switch Ruby per project. You can also start from the official Ruby installation guide.
Once the Ruby is installed, make sure it’s available by checking the version:
ruby -vExample output:
ruby 4.0.2 (2026-03-17 revision d3da9fec82) +PRISM [arm64-darwin25] -
Rails is installed.
To generate a new app, you need Rails already installed globally. Make sure to have that ready. Once the Ruby is setup you can install latest version of Rails with
gem install rails.You can check the version of Rails with:
gem install rails rails -vExample output:
Rails 8.1.3
What you will do in this chapter #
- You will generate the Recipes app
- Install gems
- Prepare the test database
- Run the default Minitest suite so you trust your machine before we talk theory in the next chapter
By the end you should get green output from bin/rails test:all at the root of your recipes app.
Generate the Recipes app #
Onto the exciting part, we will generate a new Rails app for Recipes that we will be using for adding features and tests.
- Open a terminal.
-
cdto the folder where you keep projects (change the path to match your machine):cd ~/projects -
Confirm where you are:
pwdYou should see the path you expect. The next command will create a sibling folder next to whatever else lives here.
-
Generate the app
rails new recipesrails newcreates a directory namedrecipesand fills it with a full Rails skeleton.
What each part of the command means #
| Part | Meaning |
|---|---|
rails new |
Rails generator. Creates a new application skeleton. |
recipes |
Directory name and default module name. That is the Recipes app for the rest of the guide. |
What you should see #
When the generator finishes, list the new folder:
ls recipes
You should see app/, config/, db/, test/, Gemfile, config.ru, and other usual Rails files.
This means Rails app is now setup.
System test gems in the Gemfile #
Open Gemfile and find the group :test do block. It should include:
group :test do
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
gem "capybara"
gem "selenium-webdriver"
end
Ensure these two gems are inside the Gemfile, they are required for system tests to work properly.
NOTE: If those lines are missing, add them under group :test, save, then run bundle install again.
First git commit #
This step is optional but recommended. Make a first commit so you can roll back if a later step goes wrong:
cd recipes
git status
git add .
git commit -m "Initialize Recipes app"
What is inside test/ #
Let’s take a look at files and folders inside the test folder. To do that, hit the following command from the app root:
ls test
You should always see at least:
| Path | Role |
|---|---|
test/test_helper.rb |
Boots test, loads rails/test_help, shared Minitest setup (for example fixtures and parallel workers). |
test/controllers/ |
Controller tests when you use them. |
test/models/ |
Model tests: validations, scopes, plain Ruby on your models. |
test/integration/ |
Integration tests: full HTTP cycle through routing, controller, and often views. |
test/fixtures/ |
YAML fixtures for sample rows. |
test/mailers/ |
Mailer tests. |
test/helpers/ |
Helper tests (we use them rarely). |
Why test/system might be empty at first #
On a plain Rails 8 app, the test/system directory and test/application_system_test_case.rb file are often not created until you generate your first system test. Maintainers summarized the default like this:
System tests are a nice to have but don’t make sense to be generated by default since you don’t want to test every endpoint this way. They should be used for testing critical paths, especially where JS is involved.
We will still use system tests where they earn their keep: critical flows and smoke checks that the app boots in a real browser.
Don’t worry about missing folder and file yet, we will get both of them when we generate one system test in the app later in this chapter, we will talk more about these files and folder there. For more detail about the removal decision of system tests from defaults, you can check following pull requests in the Rails repository:
Prepare the test database #
Rails uses a separate database for RAILS_ENV=test. Prepare it once so you do not chase connection errors during your first run:
RAILS_ENV=test bin/rails db:prepare
What that does:
- Loads the app in test
- Creates the test database if it is missing
- Applies the schema so it matches
db/schema.rb
Run non-system tests #
bin/rails test
That runs everything under test/ except files inside test/system. Use it when you want a fast loop without booting the browser.
How to read the output #
Example shape:
Run options: --seed 12345
# Running:
..
Finished in 0.012345s, 161.8 runs/s, 161.8 assertions/s.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
- runs — how many
test "..." doblocks ran - assertions — how many
assert_*calls ran - failures — failed assertions
- errors — unexpected exceptions
- skips — tests you marked skipped with
skip "this test"inside the test block
A brand new app ships a small number of sample tests. The exact counts are not important. You want 0 failures and 0 errors.
Add system tests (generator + smoke test) #
Right now we don’t have any system test setup because Rails 8 doesn’t ship that by default as already noted in previous sections. So, let’s now setup the system test and add one simple system test to ensure it’s working.
Generate a system test scaffold:
bin/rails generate system_test rails_default_pages
That should create (names matter):
| File | Role |
|---|---|
test/application_system_test_case.rb |
Base class for every system test: Capybara + Selenium driver. |
test/system/rails_default_pages_test.rb |
First system test file (we replace its body in a moment). |
What is inside test/application_system_test_case.rb #
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ]
end
require "test_helper"— same boot path as other tests (ENV,config/environment, Minitest helpers).ApplicationSystemTestCase— inheritsActionDispatch::SystemTestCase, Rails wrapper around Capybara + a driver.driven_by :selenium, using: :headless_chrome, ...— runs Chrome without opening a window. Good for day to day runs.
NOTE: You need a working Chrome or Chromium install for system tests to work. If bin/rails test:system fails with a driver error, install Chrome and retry.
Smoke test for /up #
A new Rails app already exposes the health check at /up (see config/routes.rb). That gives a boring, stable URL for your first visit.
Replace test/system/rails_default_pages_test.rb with:
require "application_system_test_case"
class RailsDefaultPagesTest < ApplicationSystemTestCase
test "health check responds over the browser" do
visit "/up"
assert_current_path "/up"
end
end
Line by line:
test "..." do— names the example in Minitest output.visit "/up"— Capybara drives the browser to that path on the test server (not necessarily port 3000; Rails picks a free port).assert_current_path "/up"— Checks if the URL in the browser equals/upor not. Fails the test if the URL doesn’t match.
Run system tests only:
bin/rails test:system
You want 0 failures and 0 errors. Assertion count can differ slightly by Rails version; the important part is green.
You have a real browser test running now, not just theory, and that is exactly the kind of milestone this guide is built around.
You just shipped your first system smoke test.
Getting system tests green on a fresh app is where many guides hand-wave. If this chapter got you past that setup wall, fund the next chapter and keep the guide free.
One-time support via Stripe. No account required.
Run everything together #
You can also run all tests (unit, integration, and system) in one go. On a fresh app it is a quick confidence check before you start building features. You can run them all with the following command:
bin/rails test:all
That runs non-system tests and test/system in one go. After this chapter you should see at least the default tests plus your /up system test in the combined output.
Commit your work #
Once bin/rails test:all is green, save what you added in this chapter:
git add .
git commit -m "Add system tests and /up health check smoke test"
That commit usually includes the Capybara gems in your Gemfile, test/application_system_test_case.rb, and test/system/rails_default_pages_test.rb. Small checkpoints like this make it easier to roll back one step or see exactly when something broke.
If you already made the optional Initialize Recipes app commit earlier, treat this as your second checkpoint. If you skipped that step, you can commit twice (initialize, then system tests) or use one message that covers both. Either way, get a green bin/rails test:all on disk before you continue.
End of Chapter 1 #
You now have:
- A Recipes app with Minitest under
test/ - Capybara and Selenium ready for system tests
- A green starting point for the Recipes app that you will begin building in chapter 5
- A green
bin/rails test:allon your machine
You have not built recipe pages, validations, or mailers yet. That is intentional. Chapters 2 through 4 are vocabulary and approach (map, tools, scenarios). Chapter 5 is the first time you add Recipe code and automated tests.
When you are ready, continue to Types of Tests in the Rails World.
Keep Minitest Rails independent
Minitest Rails is an independent educational guide for Rails developers learning automated testing.
Reader support funds new chapters, Rails version updates, and more real-world examples.
Something unclear in this chapter?
Send feedbackDisclaimer: This guide is written in tandem with AI but reviewed and enhanced by me based on my experience with Rails and testing. I stand by the advice and patterns here.