Skip to content

Refactor: Split Assert.AreEquivalent.Comparer.cs (1248 lines) into focused files #8332

@Evangelink

Description

@Evangelink

Overview

The file src/TestFramework/TestFramework/Assertions/Assert.AreEquivalent.Comparer.cs has grown to 1248 lines, making it harder to navigate and maintain. This task involves refactoring it into smaller, more focused files.

Current State

  • File: src/TestFramework/TestFramework/Assertions/Assert.AreEquivalent.Comparer.cs
  • Size: 1248 lines
  • Language: C#
Structural Analysis

The file is a partial class Assert containing a large private EquivalenceComparer class and several supporting nested types. The key groups are:

  • EquivalenceComparer (lines ~13–853): The core comparison engine with methods for comparing primitives, enumerables, dictionaries, and object members via reflection. Also contains cycle-detection logic and IEquatable<T> invocation.
  • ReferenceObjectComparer (lines ~854–869): Tiny IEqualityComparer<object> used for reference-equality cycle tracking.
  • MemberLookup (lines ~871–888): Caches reflected properties/fields of a type.
  • DictionaryView / NonGenericDictionaryView / GenericDictionaryView / GenericDictionaryAccessors (lines ~890–1067): Abstraction layer that wraps both IDictionary and IDictionary<TKey,TValue> for uniform access.
  • MemberAccessor (lines ~1068–1098): Wraps a PropertyInfo or FieldInfo into a uniform getter delegate.
  • EquivalenceMismatch (lines ~1099–1248): Immutable result type describing a mismatch, including path, reason, and formatted expected/actual values.

Refactoring Strategy

Proposed File Splits

Based on the file's structure, split it into the following files (all nested inside the existing partial class Assert or extracted as file-scoped private types in a new partial file):

  1. Assert.AreEquivalent.Mismatch.cs

    • Contents: EquivalenceMismatch sealed class
    • Responsibility: Immutable result DTO — path, reason, expected/actual text, comparison-failure flag, and message formatting
  2. Assert.AreEquivalent.DictionaryView.cs

    • Contents: DictionaryView (abstract), NonGenericDictionaryView, GenericDictionaryView, GenericDictionaryAccessors, DictionaryLookup
    • Responsibility: Uniform read-only dictionary abstraction over both generic and non-generic IDictionary types
  3. Assert.AreEquivalent.MemberAccessor.cs

    • Contents: MemberAccessor, MemberLookup, ReferenceObjectComparer
    • Responsibility: Reflection-based member discovery and cached accessor delegates
  4. Assert.AreEquivalent.Comparer.cs (trimmed)

    • Contents: EquivalenceComparer — core comparison logic only, now that all helper types live in their own files
    • Responsibility: Orchestrate deep-equality comparison using the types defined in the other files

Implementation Guidelines

  1. Preserve Behavior: All existing functionality must work identically after the split
  2. Maintain Public API: The public AreEquivalent / AreNotEquivalent methods (defined elsewhere) must remain unchanged
  3. Use file-scoped types or keep nesting: Since these types are private implementation details, they can remain as nested classes inside partial class Assert across multiple partial files — no visibility change required
  4. Test After Each Split: Run dotnet test after each incremental file extraction
  5. One File at a Time: Extract one group of types at a time to make review easier

Acceptance Criteria

  • Original file is split into focused modules (each under 300 lines)
  • Each new file is under 300 lines
  • All tests pass after refactoring
  • No breaking changes to the public API
  • No changes to the public Assert.AreEquivalent / Assert.AreNotEquivalent method signatures

Priority: Medium
Effort: Small — all types are self-contained; no external callers need updating beyond within the same partial class
Expected Impact: Improved code navigability, easier testing of individual components, reduced merge conflicts on the Assert partial class files

Generated by Daily File Diet · ● 2M ·

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions