Back to Repositories

Testing NoObject Version Management in PaperTrail

This test suite validates the behavior of PaperTrail versioning when working with models that don’t store object data. It specifically tests version record creation, reification, and querying functionality when the object column is absent from version records.

Test Coverage Overview

The test suite provides comprehensive coverage of PaperTrail’s versioning capabilities when object data is not stored:

  • Version record creation for create/update/destroy operations
  • Verification of object_changes data structure
  • Handling of metadata persistence
  • YAML serialization and deserialization

Implementation Analysis

The testing approach uses RSpec’s describe/it blocks to organize test cases logically. It employs expectations to verify version attributes, YAML parsing, and error handling. The implementation leverages RSpec’s versioning: true shared context and makes extensive use of let statements and before blocks for setup.

Technical Details

  • RSpec for test framework
  • YAML for serialization testing
  • PaperTrail gem configuration
  • Custom error handling validation
  • Attribute-level change tracking

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated test cases with clear purpose
  • Proper error scenario coverage
  • Comprehensive attribute verification
  • Consistent use of expectations
  • Clear test organization and structure

paper-trail-gem/paper_trail

spec/models/no_object_spec.rb

            
# frozen_string_literal: true

require "spec_helper"

RSpec.describe NoObject, versioning: true do
  it "still creates version records" do
    n = described_class.create!(letter: "A")
    a = n.versions.last.attributes
    expect(a).not_to include "object"
    expect(a["event"]).to eq "create"
    expect(a["object_changes"]).to start_with("---")
    expect(a["metadatum"]).to eq(42)

    n.update!(letter: "B")
    a = n.versions.last.attributes
    expect(a).not_to include "object"
    expect(a["event"]).to eq "update"
    expect(a["object_changes"]).to start_with("---")
    expect(a["metadatum"]).to eq(42)

    n.destroy!
    a = n.versions.last.attributes
    expect(a).not_to include "object"
    expect(a["event"]).to eq "destroy"
    expect(a["object_changes"]).to start_with("---")
    expect(a["metadatum"]).to eq(42)

    # New feature: destroy populates object_changes
    # https://github.com/paper-trail-gem/paper_trail/pull/1123
    h = if ::YAML.respond_to?(:unsafe_load)
          YAML.unsafe_load a["object_changes"]
        else
          YAML.load a["object_changes"]
        end
    expect(h["id"]).to eq([n.id, nil])
    expect(h["letter"]).to eq([n.letter, nil])
    expect(h["created_at"][0]).to be_present
    expect(h["created_at"][1]).to be_nil
    expect(h["updated_at"][0]).to be_present
    expect(h["updated_at"][1]).to be_nil
  end

  describe "reify" do
    it "raises error" do
      n = described_class.create!(letter: "A")
      v = n.versions.last
      expect { v.reify }.to(
        raise_error(
          ::PaperTrail::Error,
          "reify requires an object column"
        )
      )
    end
  end

  describe "where_object" do
    it "raises error" do
      n = described_class.create!(letter: "A")
      expect {
        n.versions.where_object(foo: "bar")
      }.to(
        raise_error(
          ::PaperTrail::Error,
          "where_object requires an object column"
        )
      )
    end
  end
end