To Infinity and Beyond!
Infinity Test is a continuous testing library and a flexible alternative to Autotest and Guard. It watches your files for changes and automatically runs your tests, providing instant feedback with desktop notifications.
Version 2.0.0 brings a complete rewrite with modern dependencies, multi-Ruby support via RVM/RbEnv, a powerful callbacks system, and experimental support for Elixir (Phoenix, ExUnit), Python (Django, FastAPI, Pytest), and Rust (Rocket, Cargo).
- Installation
- Quick Start
- Command Line Options
- Configuration File (INFINITY_TEST)
- Ruby Version Managers
- Test Frameworks
- Application Frameworks
- Experimental: Elixir Support
- Experimental: Python Support
- Experimental: Rust Support
- Notifications
- Image Themes
- Callbacks
- File Watching
- Advanced Usage
Since this is a release candidate (rc1), you need to explicitly request the pre-release version:
gem install infinity_test --preOr add to your Gemfile:
gem 'infinity_test', '~> 2.0.0.rc2', group: :developmentThen run:
bundle installcd /path/to/your/projectinfinity_testThat's it! Infinity Test will:
- Auto-detect your test framework (RSpec, Test::Unit, ExUnit, or Pytest)
- Auto-detect your application framework (Rails, Padrino, Rubygems, Phoenix, Django, FastAPI, etc.)
- Run all tests immediately
- Watch for file changes and re-run relevant tests
- Show desktop notifications with test results
Edit your files and watch the tests run automatically!
| Option | Short | Description |
|---|---|---|
--ruby strategy |
Ruby manager strategy: auto_discover, rvm, rbenv, ruby_default |
|
--rubies=versions |
Ruby versions to test against (comma-separated) | |
--test library |
Test framework: auto_discover, rspec, test_unit, ex_unit, pytest, cargo_test |
|
--framework library |
Application framework: auto_discover, rails, padrino, rubygems, phoenix, elixir_mix, django, fast_api, python_package, rocket, rust_cargo |
|
--options=options |
Additional options to pass to test command | |
--notifications library |
Notification system: auto_discover, osascript, terminal_notifier, notify_send, dunstify |
|
--mode theme |
Image theme for notifications (see Image Themes) | |
--no-infinity-and-beyond |
-n |
Run tests once and exit (CI mode) |
--just-watch |
-j |
Skip initial test run, only watch for changes |
--focus [FILE] |
-f |
Focus on specific tests or failures for last failed tests |
--no-verbose |
Don't print commands before executing | |
--no-bundler |
Bypass Bundler support | |
--help |
Show help message |
Run with RSpec only:
infinity_test --test rspecRun tests on multiple Ruby versions with RVM:
infinity_test --ruby rvm --rubies=3.0.0,3.1.0,3.2.0Run tests once and exit (for CI):
infinity_test -nSkip initial test run, just watch:
infinity_test --just-watchFocus on failed tests:
infinity_test --focus failuresFocus on a specific file:
infinity_test --focus spec/models/user_spec.rbUse a specific notification theme:
infinity_test --mode mario_brosPass additional options to the test runner:
infinity_test --options="-Ilib -Itest"You can persist your settings in an INFINITY_TEST file. Infinity Test looks for configuration in two locations:
- Project file:
./INFINITY_TEST(higher priority) - Global file:
~/INFINITY_TEST(lower priority)
InfinityTest.setup do |config|
config.test_framework = :rspec
config.framework = :rails
config.notifications = :terminal_notifier
config.mode = :mario_bros
config.verbose = true
endInfinityTest.setup do |config|
# Ruby Version Manager
# Options: :auto_discover, :rvm, :rbenv, :ruby_default
config.strategy = :auto_discover
# Ruby versions to test against (requires RVM or RbEnv)
config.rubies = ['3.0.0', '3.1.0', '3.2.0']
# Gemset to use with RVM
config.gemset = 'my_project'
# Test framework
# Options: :auto_discover, :rspec, :test_unit, :ex_unit, :pytest, :cargo_test
config.test_framework = :rspec
# Application framework
# Options: :auto_discover, :rails, :padrino, :rubygems, :phoenix, :elixir_mix, :django, :fast_api, :python_package, :rocket, :rust_cargo
config.framework = :rails
# File observer
# Options: :listen (event-driven), :filewatcher (polling)
config.observer = :listen
# Notification system
# Options: :auto_discover, :osascript, :terminal_notifier, :notify_send, :dunstify
config.notifications = :auto_discover
# Image theme for notifications
config.mode = :simpson
# Or use custom images
config.success_image = '/path/to/success.png'
config.failure_image = '/path/to/failure.png'
config.pending_image = '/path/to/pending.png'
# Bundler support (auto-detected if Gemfile exists)
config.bundler = true
# Print commands before executing
config.verbose = true
# File extension to watch
config.extension = :rb
# Skip initial test run
config.just_watch = false
# Run tests and exit (CI mode)
config.infinity_and_beyond = true
# Focus mode: nil, :failures, or file path
config.focus = nil
# Ignore specific test files
config.ignore_test_files = [
'spec/slow/performance_spec.rb'
]
# Ignore test folders
config.ignore_test_folders = [
'spec/integration'
]
endInfinityTest.setup do |config|
config.test_framework = :rspec
config.mode = :faces
end
# Clear terminal before running tests
InfinityTest::Core::Base.before(:all) do
InfinityTest::Core::Base.clear_terminal
end
# Run after all tests complete
InfinityTest::Core::Base.after(:all) do
system('say "Tests finished"')
end
# Run before each Ruby version (when testing multiple rubies)
InfinityTest::Core::Base.before(:each_ruby) do |environment|
puts "Testing with Ruby #{environment[:ruby_version]}"
end
# Run after each Ruby version
InfinityTest::Core::Base.after(:each_ruby) do |environment|
puts "Finished testing Ruby #{environment[:ruby_version]}"
endInfinity Test supports testing across multiple Ruby versions using RVM or RbEnv.
config.strategy = :auto_discoverPriority order: RVM > RbEnv > RubyDefault
Test against multiple Ruby versions with optional gemset support:
InfinityTest.setup do |config|
config.strategy = :rvm
config.rubies = ['3.0.0', '3.1.0', 'jruby-9.4.0.0']
config.gemset = 'infinity_test' # Optional
endCommand line:
infinity_test --ruby rvm --rubies=3.0.0,3.1.0,jruby-9.4.0.0Test against multiple Ruby versions using rbenv:
InfinityTest.setup do |config|
config.strategy = :rbenv
config.rubies = ['3.0.0', '3.1.0', '3.2.0']
endCommand line:
infinity_test --ruby rbenv --rubies=3.0.0,3.1.0Use the current Ruby version only (no version manager):
config.strategy = :ruby_defaultAuto-detected when spec/ directory exists with spec_helper.rb or *_spec.rb files.
config.test_framework = :rspecAuto-detected when test/ directory exists with test_helper.rb or *_test.rb files.
config.test_framework = :test_unitSee Elixir Support for ExUnit and Python Support for Pytest.
Auto-detected when config/environment.rb exists.
Watched directories:
app/models→ runs corresponding model specs/testsapp/controllers→ runs corresponding controller specs/testsapp/helpers→ runs corresponding helper specs/testsapp/mailers→ runs corresponding mailer specs/testsapp/jobs→ runs corresponding job specs/testslib/→ runs corresponding lib specs/testsspec/ortest/→ runs the changed spec/test filespec_helper.rbortest_helper.rb→ runs all tests
Auto-detected when config/boot.rb exists with Padrino reference.
Similar heuristics to Rails with Padrino-specific paths.
Used for gem development and simple Ruby projects.
Watched directories:
lib/→ runs corresponding specs/testsspec/ortest/→ runs the changed spec/test file
Note: Elixir support is experimental. Please report any issues.
Infinity Test can watch Elixir projects and run ExUnit tests automatically.
Auto-detected when test/ directory exists with test_helper.exs or *_test.exs files.
config.test_framework = :ex_unitAuto-detected when mix.exs exists and lib/*_web/ directory is present.
Watched directories:
lib/my_app/→ runs corresponding tests intest/my_app/lib/my_app_web/controllers/→ runs corresponding controller testslib/my_app_web/live/→ runs corresponding LiveView teststest/→ runs the changed test filetest/test_helper.exs→ runs all tests
config.framework = :phoenixFor standard Elixir/Mix projects (non-Phoenix). Auto-detected when mix.exs exists.
Watched directories:
lib/→ runs corresponding teststest/→ runs the changed test file
config.framework = :elixir_mixInfinityTest.setup do |config|
config.test_framework = :ex_unit
config.framework = :phoenix # or :elixir_mix
config.strategy = :elixir_default
config.notifications = :terminal_notifier
endNote: Python support is experimental. Please report any issues.
Infinity Test can watch Python projects and run Pytest tests automatically.
Auto-detected when tests/ or test/ directory exists with test_*.py or *_test.py files.
config.test_framework = :pytestOutput parsing:
5 passed→ success1 failed, 4 passed→ failure4 passed, 1 skipped→ pending
Auto-detected when manage.py exists with Django imports.
Watched directories:
- Django app directories (containing
models.py,views.py, orapps.py) - Each app's
tests/directory ortests.pyfile conftest.py→ runs all tests
config.framework = :djangoAuto-detected when main.py (or app/main.py, src/main.py) contains FastAPI imports. Ideal for ML model serving APIs.
Watched directories:
app/→ API application coderouters/→ API route definitionsapi/→ API endpointsendpoints/→ endpoint handlerstests/→ test files
config.framework = :fast_apiFor standard Python packages. Auto-detected when pyproject.toml, setup.py, or setup.cfg exists.
Watched directories:
src/→ source codelib/→ library code- Package directories (containing
__init__.py) tests/ortest/→ test files
config.framework = :python_packageInfinityTest.setup do |config|
config.test_framework = :pytest
config.framework = :fast_api # or :django, :python_package
config.strategy = :python_default
config.notifications = :terminal_notifier
endFastAPI ML API:
my_ml_api/
├── app/
│ ├── main.py # from fastapi import FastAPI
│ ├── models/ # ML models
│ └── routers/ # API endpoints
├── tests/
│ ├── conftest.py
│ └── test_api.py
└── pyproject.toml
Django ML Dashboard:
my_dashboard/
├── manage.py
├── dashboard/ # Django app
│ ├── models.py
│ ├── views.py
│ └── tests/
├── ml_pipeline/ # Django app
│ ├── models.py
│ └── tests.py
└── requirements.txt
Note: Rust support is experimental. Please report any issues.
Infinity Test can watch Rust projects and run cargo test automatically.
Auto-detected when Cargo.toml exists.
config.test_framework = :cargo_testOutput parsing:
5 passed; 0 failed; 0 ignored→ success3 passed; 2 failed; 0 ignored→ failure4 passed; 0 failed; 1 ignored→ pending
Auto-detected when Cargo.toml contains rocket dependency. For Rocket web applications.
Watched directories:
src/*.rs→ runs tests matching the module name (e.g.,src/routes.rs→cargo test routes)src/lib.rsorsrc/main.rs→ runs all teststests/*.rs→ runs the specific integration testCargo.toml→ runs all testsRocket.toml→ runs all tests (if exists)
config.framework = :rocketFor standard Rust libraries and applications. Auto-detected when Cargo.toml exists (and no Rocket dependency).
Watched directories:
src/*.rs→ runs tests matching the module nametests/*.rs→ runs the specific integration testCargo.toml→ runs all tests
config.framework = :rust_cargoInfinityTest.setup do |config|
config.test_framework = :cargo_test
config.framework = :rust_cargo # or :rocket
config.strategy = :rust_default
config.notifications = :terminal_notifier
endmy_rust_project/
├── Cargo.toml
├── src/
│ ├── lib.rs # Changes run all tests
│ ├── user.rs # Changes run `cargo test user`
│ └── utils.rs # Changes run `cargo test utils`
└── tests/
├── integration.rs # Changes run `cargo test --test integration`
└── api_tests.rs # Changes run `cargo test --test api_tests`
Desktop notifications show test results with themed images.
| System | Platform | Description |
|---|---|---|
auto_discover |
All | Automatically detect available notifier |
terminal_notifier |
macOS | Modern macOS notifications |
osascript |
macOS | Built-in AppleScript notifications |
notify_send |
Linux | libnotify notifications |
dunstify |
Linux | Dunst notification daemon |
Configuration:
config.notifications = :terminal_notifierCommand line:
infinity_test --notifications terminal_notifierInfinity Test includes fun image themes for notifications. Each theme has three images: success, failure, and pending.
| Theme | Description | Location |
|---|---|---|
simpson |
The Simpsons characters (default) | images/simpson/ |
faces |
Expressive face icons | images/faces/ |
fuuu |
Rage comic faces | images/fuuu/ |
hands |
Hand gesture icons | images/hands/ |
mario_bros |
Super Mario characters | images/mario_bros/ |
rails |
Ruby on Rails themed | images/rails/ |
rubies |
Ruby gemstone themed | images/rubies/ |
street_fighter |
Street Fighter characters | images/street_fighter/ |
toy_story |
Toy Story characters | images/toy_story/ |
Configuration file:
config.mode = :mario_brosCommand line:
infinity_test --mode mario_brosUse your own images by setting the image paths directly:
InfinityTest.setup do |config|
config.success_image = '/path/to/my_success.png'
config.failure_image = '/path/to/my_failure.png'
config.pending_image = '/path/to/my_pending.png'
endOr point to a custom directory containing success.*, failure.*, and pending.* images:
config.mode = '/path/to/my/images/directory'Callbacks let you run custom code before or after tests.
| Callback | Scope | Description |
|---|---|---|
before(:all) |
All | Runs once before all tests |
after(:all) |
All | Runs once after all tests |
before(:each_ruby) |
Per Ruby | Runs before testing each Ruby version |
after(:each_ruby) |
Per Ruby | Runs after testing each Ruby version |
Clear terminal before tests:
InfinityTest::Core::Base.before(:all) do
InfinityTest::Core::Base.clear_terminal
endPlay a sound after tests:
InfinityTest::Core::Base.after(:all) do
system('afplay /path/to/sound.mp3')
endLog Ruby version being tested:
InfinityTest::Core::Base.before(:each_ruby) do |env|
File.write('test.log', "Testing: #{env[:ruby_version]}\n", mode: 'a')
endCompile extensions before each Ruby:
InfinityTest::Core::Base.before(:each_ruby) do |env|
system('rake compile')
endEvent-driven file watching using native OS notifications. Fast and efficient.
config.observer = :listenPolling-based file watching. Works everywhere including VMs and NFS mounts.
config.observer = :filewatcherRun tests once and exit with proper exit code:
infinity_test -nOr in configuration:
config.infinity_and_beyond = falseSkip the initial test run and only watch for changes. Useful for large projects:
infinity_test --just-watchRun only specific tests:
# Run only previously failed tests
infinity_test --focus failures
# Run only a specific file
infinity_test --focus spec/models/user_spec.rbInfinityTest.setup do |config|
# Ignore specific test files
config.ignore_test_files = [
'spec/slow/large_integration_spec.rb',
'spec/external/api_spec.rb'
]
# Ignore entire directories
config.ignore_test_folders = [
'spec/integration',
'spec/system'
]
end# Watch Python files
config.extension = :py
# Watch JavaScript files
config.extension = :js# INFINITY_TEST
InfinityTest.setup do |config|
# Multi-Ruby testing with RVM
config.strategy = :rvm
config.rubies = ['3.1.0', '3.2.0', '3.3.0']
config.gemset = 'myapp'
# Test framework and app framework
config.test_framework = :rspec
config.framework = :rails
# Notifications with Mario theme
config.notifications = :terminal_notifier
config.mode = :mario_bros
# Performance settings
config.observer = :listen
config.just_watch = false
config.verbose = true
# Ignore slow tests during development
config.ignore_test_folders = ['spec/system']
end
# Clear screen before each run
InfinityTest::Core::Base.before(:all) do
InfinityTest::Core::Base.clear_terminal
end
# Notify via system sound when done
InfinityTest::Core::Base.after(:all) do
system('afplay /System/Library/Sounds/Glass.aiff')
end- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Run the tests (
infinity_test) - Commit your changes (
git commit -am 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Create a Pull Request
MIT License - see LICENSE.txt for details.
Tomas D'Stefano
Happy Testing!