Back to Repositories

Testing Single Table Inheritance Slug Generation in friendly_id

This test suite validates Single Table Inheritance (STI) functionality in FriendlyId, focusing on slug generation and lookup across inherited model classes. It ensures proper configuration inheritance and unique slug generation for parent and child classes in Ruby on Rails applications.

Test Coverage Overview

The test suite comprehensively covers Single Table Inheritance scenarios in FriendlyId implementation.

Key areas tested include:
  • Configuration inheritance between parent and child classes
  • Unique slug generation across inherited models
  • Slug collision prevention between parent and child records
  • Integration with FriendlyId modules like :history and :finders

Implementation Analysis

The testing approach uses multiple test classes to isolate different STI scenarios and FriendlyId features. It employs minitest assertions to verify configuration inheritance, slug generation, and finder methods. The implementation leverages Ruby’s class inheritance and ActiveRecord’s STI capabilities to test various FriendlyId configurations.

Technical Details

Testing tools and setup:
  • Minitest framework for test organization
  • ActiveRecord for database interactions
  • FriendlyId::Test module for shared test functionality
  • Transaction blocks for database isolation
  • Dynamic class creation for testing abstract classes

Best Practices Demonstrated

The test suite exemplifies strong testing practices through modular test organization and comprehensive edge case coverage. Notable practices include:
  • Isolation of different feature combinations in separate test classes
  • Use of shared test modules to prevent code duplication
  • Explicit transaction handling for database cleanliness
  • Thorough validation of inheritance scenarios

norman/friendly_id

test/sti_test.rb

            
require "helper"

class StiTest < TestCaseClass
  include FriendlyId::Test
  include FriendlyId::Test::Shared::Core
  include FriendlyId::Test::Shared::Slugged

  class Journalist < ActiveRecord::Base
    extend FriendlyId
    friendly_id :name, use: [:slugged]
  end

  class Editorialist < Journalist
  end

  def model_class
    Editorialist
  end

  test "friendly_id should accept a base and a hash with single table inheritance" do
    abstract_klass = Class.new(ActiveRecord::Base) do
      def self.table_exists?
        false
      end
      extend FriendlyId
      friendly_id :foo, use: :slugged, slug_column: :bar
    end
    klass = Class.new(abstract_klass)
    assert klass < FriendlyId::Slugged
    assert_equal :foo, klass.friendly_id_config.base
    assert_equal :bar, klass.friendly_id_config.slug_column
  end

  test "the configuration's model_class should be the class, not the base_class" do
    assert_equal model_class, model_class.friendly_id_config.model_class
  end

  test "friendly_id should accept a block with single table inheritance" do
    abstract_klass = Class.new(ActiveRecord::Base) do
      def self.table_exists?
        false
      end
      extend FriendlyId
      friendly_id :foo do |config|
        config.use :slugged
        config.base = :foo
        config.slug_column = :bar
      end
    end
    klass = Class.new(abstract_klass)
    assert klass < FriendlyId::Slugged
    assert_equal :foo, klass.friendly_id_config.base
    assert_equal :bar, klass.friendly_id_config.slug_column
  end

  test "friendly_id slugs should not clash with each other" do
    transaction do
      journalist = model_class.base_class.create! name: "foo bar"
      editoralist = model_class.create! name: "foo bar"

      assert_equal "foo-bar", journalist.slug
      assert_match(/foo-bar-.+/, editoralist.slug)
    end
  end
end

class StiTestWithHistory < StiTest
  class Journalist < ActiveRecord::Base
    extend FriendlyId
    friendly_id :name, use: [:slugged, :history]
  end

  class Editorialist < Journalist
  end

  def model_class
    Editorialist
  end
end

class StiTestWithFinders < TestCaseClass
  include FriendlyId::Test

  class Journalist < ActiveRecord::Base
    extend FriendlyId
    friendly_id :name, use: [:slugged, :finders]
  end

  class Editorialist < Journalist
    extend FriendlyId
    friendly_id :name, use: [:slugged, :finders]
  end

  def model_class
    Editorialist
  end

  test "friendly_id slugs should be looked up from subclass with friendly" do
    transaction do
      editoralist = model_class.create! name: "foo bar"
      assert_equal editoralist, model_class.friendly.find(editoralist.slug)
    end
  end

  test "friendly_id slugs should be looked up from subclass" do
    transaction do
      editoralist = model_class.create! name: "foo bar"
      assert_equal editoralist, model_class.find(editoralist.slug)
    end
  end
end

class StiTestSubClass < TestCaseClass
  include FriendlyId::Test

  class Journalist < ActiveRecord::Base
    extend FriendlyId
  end

  class Editorialist < Journalist
    extend FriendlyId
    friendly_id :name, use: [:slugged, :finders]
  end

  def model_class
    Editorialist
  end

  test "friendly_id slugs can be created and looked up from subclass" do
    transaction do
      editoralist = model_class.create! name: "foo bar"
      assert_equal editoralist, model_class.find(editoralist.slug)
    end
  end
end