Testing Rails generators with ease
Testing Rails generators can be challenging, but with the right approach, you can write comprehensive test cases that reduce effort and automate your workflow. This post covers an effective strategy for testing custom Rails generators.
Why Test Generators?
Generators are often overlooked when it comes to testing, but they're critical infrastructure code. Testing generators ensures:
- Generated code works correctly
- Files are created in the right locations
- Dependencies are properly added
- Your generator doesn't break with Rails updates
Setting Up Generator Tests
Rails provides built-in support for testing generators. Create a test file in test/lib/generators/:
# test/lib/generators/my_generator_test.rb
require 'test_helper'
require 'generators/my_generator/my_generator_generator'
class MyGeneratorGeneratorTest < Rails::Generators::TestCase
tests MyGeneratorGenerator
destination Rails.root.join('tmp/generators')
setup :prepare_destination
test "generator runs without errors" do
assert_nothing_raised do
run_generator ["User"]
end
end
end
Testing File Generation
Verify that files are created correctly:
test "generates the correct files" do
run_generator ["User"]
assert_file "app/models/user.rb"
assert_file "app/controllers/users_controller.rb"
assert_file "app/views/users/index.html.erb"
end
Testing File Content
Check that generated files contain the expected content:
test "generates correct model content" do
run_generator ["User", "name:string", "email:string"]
assert_file "app/models/user.rb" do |content|
assert_match(/class User/, content)
assert_match(/validates :name/, content)
assert_match(/validates :email/, content)
end
end
Testing Gemfile Modifications
If your generator adds gems, test those additions:
test "adds required gems to Gemfile" do
run_generator ["User"]
assert_file "Gemfile" do |content|
assert_match(/gem 'some_gem'/, content)
end
end
Testing Migrations
Verify that migrations are generated correctly:
test "generates migration with correct columns" do
run_generator ["User", "name:string", "email:string"]
migration_file = Dir.glob("db/migrate/*_create_users.rb").first
assert_file migration_file do |content|
assert_match(/create_table :users/, content)
assert_match(/t\.string :name/, content)
assert_match(/t\.string :email/, content)
end
end
Using Assertions
Rails provides several useful assertions for generator testing:
assert_file: Checks if a file exists and optionally matches contentassert_no_file: Ensures a file doesn't existassert_migration: Verifies migration filesassert_gem: Checks Gemfile additions
Best Practices
- Use temporary directories: Always use
prepare_destinationto avoid polluting your test suite - Test edge cases: Test with various arguments and options
- Test rollback: If your generator supports rollback, test that too
- Keep tests focused: Each test should verify one aspect of the generator
Example: Complete Generator Test
require 'test_helper'
require 'generators/service/service_generator'
class ServiceGeneratorTest < Rails::Generators::TestCase
tests ServiceGenerator
destination Rails.root.join('tmp/generators')
setup :prepare_destination
test "generates service object" do
run_generator ["User::Create"]
assert_file "app/services/user/create.rb" do |content|
assert_match(/module User/, content)
assert_match(/class Create/, content)
assert_match(/def call/, content)
end
end
test "generates spec file" do
run_generator ["User::Create"]
assert_file "spec/services/user/create_spec.rb" do |content|
assert_match(/RSpec.describe User::Create/, content)
end
end
end
Conclusion
Testing Rails generators doesn't have to be difficult. With Rails' built-in testing support and the right approach, you can write comprehensive tests that ensure your generators work correctly and maintain quality as your application evolves.