Skip to content

Don't use Moq, use Simple or InMemory, or a local Fake#328

Merged
iancooper merged 14 commits into
masterfrom
use_simple
Jun 5, 2026
Merged

Don't use Moq, use Simple or InMemory, or a local Fake#328
iancooper merged 14 commits into
masterfrom
use_simple

Conversation

@iancooper

Copy link
Copy Markdown
Member

We don't want to use a Moq framework, instead we provide lightweight alternatives to most dependencies or build a fake for a specific context.

iancooper and others added 14 commits June 5, 2026 10:20
Migrate four legacy test files off Moq and inline nested test doubles
onto the Simple*/InMemory* doubles (ADR 0009) plus new recording test
doubles. Structural (tidy-first), test-only change.

Includes spec 007-use_simple_not_mocks: approved requirements,
requirements/design adversarial reviews, and ADR 0013 (Proposed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flip ADR 0013 status Proposed -> Accepted and record the
.design-approved marker for spec 007-use_simple_not_mocks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the approved tasks.md (Tasks 0-11) for spec 007 legacy-test migration,
the round-2 adversarial review (PASS), and the tasks-approval marker.

Amend ADR 0013 with a 2026-06-05 addendum extending Decision 4 to a
recording decorator-factory double (RecordingDecoratorFactory), so
FallbackPolicyTests' decorator Release assertions are preserved as state
rather than dropped (NFR1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds README.md to Exported/ documenting that this directory holds the
public test doubles discovered by assembly/handler scanning, distinct
from TestDoubles/ (internal doubles used directly by tests). Records the
green-suite baseline (Task 0) and ticks Tasks 0-1.

Satisfies FR9, AC10. Structural, docs-only; no .cs change in Exported/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…306)

Moves TestQuery (+ Result), TestQueryHandlerWithCatchAllFallback,
TestQueryHandlerWithFormatExceptionFallback, and
TestQueryHandlerWithoutFormatExceptionFallback verbatim into
TestDoubles/ (one type per file). FallbackPolicyTests now references
them via a using; the Moq factory/registry usage is unchanged (swapped
in Task 8). No nested double class declarations remain in the file.

Satisfies FR3 (extraction half), FR5, AC6, AC7. Structural; suite green
at baseline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tDoubles (#306)

Moves the exception-scenario queries/handlers (each Result with its
owning query), TestExceptionDecorator<,>, and DecoratorExceptionAttribute
verbatim into TestDoubles/. Private nested types promoted to internal
(attribute stays public, as before). The test references them via a
using; Moq factory/registry usage unchanged (swapped in Task 9).

Satisfies FR4 (extraction half), FR5, AC6, AC7. Structural; suite green
at baseline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds generic recording query-handler doubles deriving from
QueryHandler<,>/QueryHandlerAsync<,>: Execute/ExecuteAsync run a supplied
delegate (which may throw) and record ExecuteCount/LastQuery;
Fallback/FallbackAsync record FallbackCount then defer to base. The
CancellationToken is threaded to the delegate but not recorded
(behaviour-equivalent, FR10). Not yet referenced by any test.

Satisfies FR10 (handler-recording double), groundwork for FR1/FR2.
Structural; suite green at baseline; no src change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#306)

Adds RecordingHandlerFactory and RecordingDecoratorFactory (delegate-based
Create like the Simple* factories, plus Release recording via
ReleaseCount/Released) so the legacy Release Verify assertions migrate to
state. Adds distinct-named QueryProcessor doubles — ProcessorQuery (Guid),
ProcessorIntQuery (int), and ProcessorQueryHandler[Async] (write bag +
return Id) — copied/renamed from the Exported analogues so the two roles
stay separated (FR6, AC4, AC7). Not yet referenced by any test.

Satisfies FR10 (recording factories), FR6. Structural; suite green; no src change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
)

Replaces Mock<IQueryHandlerFactory> with RecordingHandlerFactory (fed by
a per-test handler dictionary), the decorator mock with
SimpleHandlerDecoratorFactory, and the registry mock with
InMemoryDecoratorRegistry. Interaction Verify assertions become
result-first / state reads: result.ShouldBe(id) proves the matching
handler ran with the right query; ExecuteCount/FallbackCount cover the
negatives; ReleaseCount replaces Release Verify. No Moq tokens remain; no
Exported types referenced; [Fact] count unchanged (3).

Satisfies FR1, FR7, FR10, AC2-AC5. Behaviour-preserving; suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…es (#306)

Replaces the mocks with RecordingQueryHandlerAsync doubles, a shared
SimpleHandlerDecoratorFactory + InMemoryDecoratorRegistry across both
slots, and — critically — TWO separate RecordingHandlerFactory instances
for the sync and async slots. The async pipeline creates via the async
slot but releases via the sync slot, so the async factory's
ReleaseCount(handler) is 0: the Times.Never asymmetry vs the sync file's
Times.Once is preserved (NFR1), not tidied to match. Result-first
assertions; CancellationToken not recorded. No Moq tokens; [Fact] count
unchanged (3).

Satisfies FR2, FR7, FR10, AC2-AC5. Behaviour-preserving; suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swaps the factory/registry mocks for RecordingHandlerFactory,
RecordingDecoratorFactory, and InMemoryDecoratorRegistry (fed by per-test
handler/decorator dictionaries), consuming the doubles extracted in Task
2. The three handler Release Verify and three decorator Release Verify
assertions become ReleaseCount(...).ShouldBe(1) state reads — preserved,
not dropped (NFR1; ADR 0013 D4 addendum). Outcome/bag assertions
unchanged. No Moq tokens; [Fact] count unchanged (3).

Satisfies FR3 (factory-swap half), FR7, AC1, AC2. Behaviour-preserving; suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swaps Mock<IQueryHandlerFactory> for RecordingHandlerFactory,
Mock<IQueryHandlerDecoratorFactory> for plain SimpleHandlerDecoratorFactory
(no decorator-Release assertion here), and the registry mock for
InMemoryDecoratorRegistry with real Register(...) calls. The
Release(It.IsAny<ExceptionQueryHandler>()) Times.Once check becomes
Released.OfType<ExceptionQueryHandler>().Count().ShouldBe(1). Consumes the
doubles extracted in Task 3; exception-message assertions unchanged. This
was the last Moq consumer — none remain in the test project. [Fact] count
unchanged (4).

Satisfies FR4 (factory-swap half), FR7, AC1, AC2. Behaviour-preserving; suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deletes the Moq PackageReference from Paramore.Darker.Core.Tests and the
Moq PackageVersion from Directory.Packages.props now that no test uses
Moq. Build succeeds and the full filtered suite is green at the AC11
baseline.

Satisfies FR8, AC12.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
All 14 acceptance criteria pass: no Moq anywhere (AC1/AC14), outcome
assertions preserved (AC3), no Verify/Times/It and no Exported types in
the QueryProcessor files (AC4/AC5), doubles extracted with no collisions
(AC6/AC7), Exported/*.cs and src/ untouched (AC8/AC9), README present
(AC10), [Fact] counts unchanged (AC13), suite green at baseline
(AC11/AC12). Also rewords a doc comment so AC14's case-insensitive 'moq'
sweep is clean.

Satisfies AC1-AC14, Definition of Done.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@iancooper iancooper self-assigned this Jun 5, 2026
@iancooper iancooper added .NET Pull requests that update .net code 3 - Done V5 labels Jun 5, 2026
@iancooper iancooper merged commit 27338d6 into master Jun 5, 2026
3 checks passed
@iancooper iancooper deleted the use_simple branch June 5, 2026 13:50

@codescene-delta-analysis codescene-delta-analysis Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our agent can fix these. Install it.

No application code in the PR — skipped Code Health checks.

Quality Gate Profile: Clean Code Collective
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

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

Labels

3 - Done .NET Pull requests that update .net code V5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant