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 content
  • assert_no_file: Ensures a file doesn't exist
  • assert_migration: Verifies migration files
  • assert_gem: Checks Gemfile additions

Best Practices

  1. Use temporary directories: Always use prepare_destination to avoid polluting your test suite
  2. Test edge cases: Test with various arguments and options
  3. Test rollback: If your generator supports rollback, test that too
  4. 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.

Testing Rails generators with ease - Abhay Nikam