Skip to content

Move the duplicated test doubles into tests/Shared #755

Closed

Description

@JSdotNet

The same hand-rolled fakes are declared in up to fifteen files each across the test projects, two clock approaches coexist, and tests/Shared holds a single file. Found in the 2026-09-27 repository review.

Context

Counts from grep -rl "class <Name>" tests:

  • StubGitHubClient in 14 files, StubProbe in 15, SettingsRenderContext in 12, InMemoryTaskRepository in 9, StubAzureFoundryChatClient in 6, RecordingHandler in 5 files across 3 projects, StubHttpMessageHandler in 2 projects (Capture, Sync), RecordingTelemetry in 2 projects.
  • Clocks: FixedClock in 11 files across 4 projects versus FakeTimeProvider in 45 files.
  • tests/Shared holds only RepositoryRoot.cs, linked into every project by tests/Directory.Build.props; only Backlog.UI.Components.UnitTests has a Harnesses/ folder.
  • Duplicate test class names make TRX and -class filters ambiguous: RenameRepositoryTests exists in both the Inbox and Tasks projects, MarkdownPreviewTests twice.
  • Six near-identical assertion-free "Forgetting when nothing was stored is not an error" tests in tests/Backlog.Infrastructure.FileSystem.UnitTests/*CacheTests.cs lines 142 to 274 and Backlog.Infrastructure.Claude.UnitTests/ClaudeCodeUsageCacheTests.cs line 152.
  • Polling helpers with bare Task.Delay(50) and DateTime.UtcNow deadlines in TasksDetailPaneTests.cs line 296, TasksWrittenElsewhereTests.cs line 140, RoadmapBandPaceTests.cs line 444; because they sit in private helpers, xUnit1051 never fires.
  1. Move each double to tests/Shared/TestDoubles/<Name>.cs, linked the way RepositoryRoot.cs is, and delete the copies; where copies differ, keep the superset and add the behaviour switch as a constructor argument.
  2. Standardise on FakeTimeProvider; delete FixedClock.
  3. Add one WaitUntilAsync(Func<Task<bool>>) in tests/Shared that takes TestContext.Current.CancellationToken, and replace the three hand-rolled polls.
  4. Prefix the duplicate class names with their context; wrap the assertion-free tests in Record.Exception with Assert.Null so intent is visible.

Route through delivery:flow-code per CLAUDE.md.

Activity

  1. added
    enhancementNew feature or request
    triagedClassified by the issue sweep
    in-progressA worker session is actively working this issue
    on Sep 27, 2026
  2. self-assigned this
    on Sep 29, 2026
  3. removed
    in-progressA worker session is actively working this issue
    on Sep 29, 2026
  4. removed their assignment
    on Sep 29, 2026
  5. added
    in-progressA worker session is actively working this issue
    on Sep 30, 2026
  6. self-assigned this
    on Sep 30, 2026
  7. JSdotNet commented on Sep 30, 2026

    @JSdotNet
    OwnerAuthor

    The issue sweep opened a draft pull request for this issue (linked above). Its body lists what could not be proved and what is left for later.

  8. removed
    in-progressA worker session is actively working this issue
    on Sep 30, 2026
  9. JSdotNet commented on Sep 30, 2026

    @JSdotNet
    OwnerAuthor

    Draft pull request: #904.

  10. JSdotNet commented on Oct 2, 2026

    @JSdotNet
    OwnerAuthor

    Closed by the issue sweep (2026-10-02) as already fixed: PR #904 (commit cbd8aa8, "test: share the duplicated test doubles under tests/Shared", on main). tests/Shared/TestDoubles/ now holds StubGitHubClient, StubProbe, StubAzureFoundryChatClient, InMemoryTaskRepository, RecordingHandler and RecordingTelemetry, each declared once; FixedClock is gone in favour of FakeTimeProvider; tests/Shared/Polling.cs replaces the hand-rolled polls; the duplicate class names carry a context prefix and the forget-nothing tests now assert. The commit deliberately kept SettingsRenderContext (per-test shapes), the two StubHttpMessageHandler copies (different doubles) and UnusedGitHubClient/UnusedProbe — reopen if any of those should still be shared.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requesttriagedClassified by the issue sweep

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions