Skip to content

[duplicate-code] Duplicate Code: TestingPlatformBuilderHook Boilerplate Pattern #8541

@Evangelink

Description

@Evangelink

🔍 Duplicate Code Detected: TestingPlatformBuilderHook Boilerplate Pattern

Analysis of commit 372c5f1

Summary

Detected 10+ nearly identical TestingPlatformBuilderHook classes across Platform extensions. Each class follows the same boilerplate pattern with only minor variations (namespace, method call, XML documentation). This represents significant structural duplication that increases maintenance burden and violates DRY principles.

Duplication Details

Pattern: TestingPlatformBuilderHook Boilerplate

  • Severity: High

  • Occurrences: 10 instances

  • Total Lines: ~206 lines of mostly duplicated code

  • Locations:

    • src/Platform/Microsoft.Testing.Extensions.TrxReport/TestingPlatformBuilderHook.cs
    • src/Platform/Microsoft.Testing.Extensions.AzureFoundry/TestingPlatformBuilderHook.cs
    • src/Platform/Microsoft.Testing.Extensions.Retry/TestingPlatformBuilderHook.cs
    • src/Platform/Microsoft.Testing.Extensions.Telemetry/TestingPlatformBuilderHook.cs
    • src/Platform/Microsoft.Testing.Extensions.CrashDump/TestingPlatformBuilderHook.cs
    • src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/TestingPlatformBuilderHook.cs
    • src/Platform/Microsoft.Testing.Extensions.MSBuild/TestingPlatformBuilderHook.cs
    • src/Platform/Microsoft.Testing.Extensions.HangDump/TestingPlatformBuilderHook.cs
    • src/Platform/Microsoft.Testing.Extensions.HotReload/TestingPlatformBuilderHook.cs
    • src/Platform/Microsoft.Testing.Extensions.HtmlReport/TestingPlatformBuilderHook.cs
  • Code Sample (TrxReport):

    public static class TestingPlatformBuilderHook
    {
        /// <summary>
        /// Adds TrxReport support to the Testing Platform Builder.
        /// </summary>
        /// <param name="testApplicationBuilder">The test application builder.</param>
        /// <param name="_">The command line arguments.</param>
        public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _)
            => testApplicationBuilder.AddTrxReportProvider();
    }
  • Code Sample (HangDump - with attribute variation):

    public static class TestingPlatformBuilderHook
    {
        /// <summary>
        /// Adds HangDump support to the Testing Platform Builder.
        /// </summary>
        /// <param name="testApplicationBuilder">The test application builder.</param>
        /// <param name="_">The command line arguments.</param>
        [UnsupportedOSPlatform("browser")]
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("tvos")]
        public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _)
            => testApplicationBuilder.AddHangDumpProvider();
    }

Impact Analysis

  • Maintainability: Any structural change to the hook pattern requires updating 10+ files identically
  • Bug Risk: Inconsistencies can arise when one file is updated but others are missed
  • Code Bloat: ~180+ lines of unnecessary duplication
  • Onboarding: New extensions must replicate this boilerplate, increasing friction

Refactoring Recommendations

Option 1: Source Generator (Recommended)

Create a Roslyn source generator that auto-generates TestingPlatformBuilderHook classes based on assembly-level attributes:

// In each extension assembly:
[assembly: TestingPlatformExtension(
    ExtensionMethodName = nameof(TrxReportExtensions.AddTrxReportProvider))]

// Generator produces:
public static class TestingPlatformBuilderHook
{
    public static void AddExtensions(ITestApplicationBuilder builder, string[] _)
        => builder.AddTrxReportProvider();
}

Benefits:

  • Zero manual boilerplate
  • Compile-time validation
  • Single source of truth for hook pattern
  • Automatic handling of platform attributes

Estimated effort: 8-12 hours

Option 2: Convention-Based Reflection

Use reflection in TestingPlatformAutoRegisteredExtensions to discover and invoke extension methods by convention:

// Discovery logic:
var extensionMethod = assembly.GetTypes()
    .SelectMany(t => t.GetMethods())
    .FirstOrDefault(m => m.Name.StartsWith("Add") && 
                         m.Name.EndsWith("Provider"));

Benefits:

  • Eliminates hook classes entirely
  • Runtime flexibility

Drawbacks:

  • Runtime overhead
  • Less type-safe
  • Harder to debug

Estimated effort: 4-6 hours

Option 3: Base Class with Template Method

Create an abstract base with virtual methods:

public abstract class TestingPlatformBuilderHookBase
{
    public void AddExtensions(ITestApplicationBuilder builder, string[] args)
        => AddExtensionsCore(builder, args);
    
    protected abstract void AddExtensionsCore(ITestApplicationBuilder builder, string[] args);
}

Benefits:

  • Reduces duplication
  • Maintains type safety

Drawbacks:

  • Still requires a class per extension
  • Less elegant than generator approach

Estimated effort: 2-3 hours

Implementation Checklist

  • Review duplication findings with team
  • Select refactoring approach (recommend Option 1)
  • Create design document for chosen solution
  • Implement source generator or selected approach
  • Update all existing extensions to use new pattern
  • Update documentation and extension authoring guides
  • Verify all extensions still register correctly
  • Run full integration test suite

Analysis Metadata

  • Analyzed Files: 895 C# source files
  • Detection Method: Semantic code analysis + pattern matching
  • Commit: 372c5f1
  • Analysis Date: 2026-05-25T05:45:53.799+00:00
  • Pattern Type: Structural duplication (boilerplate)

Generated by Duplicate Code Detector · ● 1.6M ·

Add this agentic workflows to your repo

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/duplicate-code-detector.md@main
  • expires on May 27, 2026, 5:53 AM UTC

Metadata

Metadata

Labels

type/automationCreated or maintained by an agentic workflow.type/tech-debtCode health, refactoring, simplification.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions