Repository navigation
Description
🔍 Duplicate Code Detected: Report Extension Registration Boilerplate
Analysis of commit cf37d3b
Assignee: @copilot
Summary
Three *ReportExtensions.cs files — CtrfReportExtensions, JUnitReportExtensions, and HtmlReportExtensions — each contain an Add{X}ReportProvider extension method that follows an identical 11-parameter CompositeExtensionFactory construction pattern and an identical three-call registration block. Beyond simple repetition, there is a consistency defect: CtrfReportExtensions is missing the if (builder is not TestApplicationBuilder) guard that both JUnitReportExtensions and HtmlReportExtensions include.
Duplication Details
Pattern: Identical 11-parameter CompositeExtensionFactory construction + registration
- Severity: Medium
- Occurrences: 3 — one per report format
- Locations:
src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportExtensions.cs(lines 24–48)src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportExtensions.cs(lines 27–55)src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportExtensions.cs(lines 27–55)
CtrfReportExtensions (missing guard):
public static void AddCtrfReportProvider(this ITestApplicationBuilder builder)
{
var commandLine = new CtrfReportGeneratorCommandLine();
var compositeCtrfReportGenerator =
new CompositeExtensionFactory<CtrfReportGenerator>(serviceProvider =>
new CtrfReportGenerator(
serviceProvider.GetConfiguration(),
serviceProvider.GetCommandLineOptions(),
serviceProvider.GetRequiredService<IFileSystem>(),
serviceProvider.GetTestApplicationModuleInfo(),
serviceProvider.GetMessageBus(),
serviceProvider.GetSystemClock(),
serviceProvider.GetEnvironment(),
serviceProvider.GetOutputDevice(),
serviceProvider.GetTestFramework(),
serviceProvider.GetTestApplicationProcessExitCode(),
serviceProvider.GetLoggerFactory().CreateLogger<CtrfReportGenerator>()));
builder.TestHost.AddDataConsumer(compositeCtrfReportGenerator);
builder.TestHost.AddTestSessionLifetimeHandler(compositeCtrfReportGenerator);
builder.CommandLine.AddProvider(() => commandLine);
}JUnitReportExtensions / HtmlReportExtensions (same structure with guard):
public static void AddJUnitReportProvider(this ITestApplicationBuilder builder)
{
if (builder is not TestApplicationBuilder)
{
throw new InvalidOperationException(ExtensionResources.InvalidTestApplicationBuilderType);
}
// ... same 11-parameter factory construction and registration ...
}Impact Analysis
- Maintainability: Adding a new report format means copy-pasting this entire block. Any change to the service-provider parameter list (e.g., adding a new injected service to
ReportGeneratorBase) requires updating all three files. This has already led to an inconsistency where CTRF lacks theTestApplicationBuilderguard. - Bug Risk: The missing
builder is not TestApplicationBuilderguard inCtrfReportExtensionsmeans CTRF report registration will silently succeed against customITestApplicationBuilderimplementations that cannot honour the extension, while JUnit and HTML fail fast. - Code Bloat: ~36 lines of near-identical factory construction repeated 3 times across 3 files.
Refactoring Recommendations
-
Immediate fix: add the missing
TestApplicationBuilderguard toCtrfReportExtensionspublic static void AddCtrfReportProvider(this ITestApplicationBuilder builder) { if (builder is not TestApplicationBuilder) { throw new InvalidOperationException(ExtensionResources.InvalidTestApplicationBuilderType); } // ... }
- Estimated effort: < 15 minutes
- Benefits: consistent guard behavior across all three report extensions
-
Extract shared factory helper to
SharedExtensionHelpersorReportGeneratorBase- Consider a static factory helper method in
SharedExtensionHelpersthat encapsulates the 11-parameter construction:// Conceptual — actual generic constraints must accommodate all three generators internal static CompositeExtensionFactory<TGenerator> CreateReportGeneratorFactory<TGenerator>(...) where TGenerator : ReportGeneratorBase<TGenerator, CapturedTestResult>
- Estimated effort: 2–4 hours
- Benefits: adding a fourth report format or changing the service-parameter list becomes a one-file change
- Consider a static factory helper method in
Implementation Checklist
- Review duplication findings
- Add missing
if (builder is not TestApplicationBuilder)guard toCtrfReportExtensions.AddCtrfReportProvider - Evaluate extracting the shared 11-parameter factory construction pattern to a helper
- Update tests that test CtrfReportExtensions with a custom builder if applicable
- Verify no functionality broken
Analysis Metadata
- Analyzed Files: 3 (
CtrfReportExtensions.cs,JUnitReportExtensions.cs,HtmlReportExtensions.cs) - Detection Method: Semantic code analysis + structural comparison
- Commit: cf37d3b
- Analysis Date: 2026-06-15
🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Duplicate Code Detector workflow. · 979.9 AIC · ⌖ 14.3 AIC · [◷]( · ◷)
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 Jun 17, 2026, 6:14 AM UTC
Activity
Copilot commentedon Jun 15, 2026
The agent encountered an error and was unable to start working on this issue: Please try again later, or contact support if the problem persists. (Request id: 1080:10ED13:43FA63C:FD8C3D1:6A2F9843)
Fix duplicate code in report extensions (microsoft#9129, microsoft#9131…
Reduce duplication in CTRF/JUnit/HTML report extensions (#9129, #9131, …
Extract shared report-provider registration helper (#9131) (#9152)