Standard collaboration patterns for all squad agents — worktree awareness, decisions, cross-agent communication
日本語の概要は準備中です。原文の説明を表示しています。
How to update Moq mock setups when a manager method gains new parameters — prevents silent overload mismatch bugs that cause MockException or wrong test results
インストールする前に、エージェントに与えられる指示の中身を確認できます。
When a manager interface method gains new parameters (e.g., ownerOid added as part of Epic #609's per-user ownership enforcement), every existing Moq .Setup() call that targets the old signature becomes a dead setup. Moq will either:
null for reference types, 0 for value types, false for bool). The test passes for the wrong reason, or fails with a NullReferenceException that is hard to diagnose.MockException — when .Verifiable() is in play and the expected call never fired, the verify step throws.This is a silent failure mode: the test compiles, may even pass, but is not testing what you think it's testing.
Project: JJGNET Broadcasting
Framework: xUnit + Moq 4.20.72 + FluentAssertions
Trigger: Any manager method signature change (new parameter added, parameter type changed, parameter removed)
GetAllAsync gains ownerOid parameterBefore Epic #609, the manager interface was:
// IScheduledItemManager — BEFORE
Task<PagedResult<ScheduledItem>> GetAllAsync(int page, int pageSize);
After Epic #609, a new overload was added for owner-filtered access:
// IScheduledItemManager — AFTER (two overloads)
Task<PagedResult<ScheduledItem>> GetAllAsync(int page, int pageSize); // admin/unfiltered
Task<PagedResult<ScheduledItem>> GetAllAsync(string ownerOid, int page, int pageSize, ...); // owner-filtered
The controller now calls the owner-filtered overload for non-admin users:
// SchedulesController (after Epic #609)
var result = await _scheduledItemManager.GetAllAsync(currentUserOid, page, pageSize);
The old mock setup targets the unfiltered overload (2 parameters). The controller now calls the owner-filtered overload (3+ parameters). Moq finds no matching setup.
// ❌ BROKEN — targets old 2-parameter overload; controller now calls 3-parameter overload
_scheduledItemManagerMock
.Setup(m => m.GetAllAsync(It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync(new PagedResult<ScheduledItem> { Items = items, TotalCount = 1 });
var sut = CreateSut(Domain.Scopes.Schedules.All, ownerOid: "test-oid-12345");
var result = await sut.GetScheduledItemsAsync();
// result.Value is null — the controller called the 3-parameter overload, no setup matched.
// The test may fail with NullReferenceException or, worse, produce a false positive.
result.Value.Should().NotBeNull(); // ← throws NullReferenceException
Symptom: NullReferenceException, MockException, or (worst case) a test that "passes" because the null result flows through a code path that returns an empty 200 OK.
Match the overload that the controller actually calls. Use It.IsAny<T>() for the new parameter unless the test specifically needs to assert its value.
// ✅ FIXED — targets the 3-parameter owner-filtered overload that the controller now calls
_scheduledItemManagerMock
.Setup(m => m.GetAllAsync(
It.IsAny<string>(), // ownerOid — new parameter; use IsAny<> unless test verifies it
It.IsAny<int>(), // page
It.IsAny<int>())) // pageSize
.ReturnsAsync(new PagedResult<ScheduledItem> { Items = items, TotalCount = 1 });
var sut = CreateSut(Domain.Scopes.Schedules.All, ownerOid: "test-oid-12345");
var result = await sut.GetScheduledItemsAsync();
// Now resolves correctly — setup matches the actual call.
result.Value.Should().NotBeNull();
result.Value!.Items.Should().HaveCount(1);
Use It.IsAny<string>() for the new parameter in most tests. Use an explicit value only when the test is specifically verifying that the controller passes the correct OID:
// ✅ Verifying that the controller passes the caller's OID to the manager
_scheduledItemManagerMock
.Setup(m => m.GetAllAsync(
"test-oid-12345", // exact OID — this test verifies the controller uses the caller's OID
It.IsAny<int>(),
It.IsAny<int>()))
.ReturnsAsync(new PagedResult<ScheduledItem> { Items = items, TotalCount = 1 });
// Then verify after Act:
_scheduledItemManagerMock.Verify(
m => m.GetAllAsync("test-oid-12345", It.IsAny<int>(), It.IsAny<int>()),
Times.Once);
Default rule: Use It.IsAny<T>() for new parameters unless there is an explicit test objective around that parameter's value.
When a manager method signature changes:
Find all affected .Setup() calls:
Select-String -Path ".\src\**\*Tests.cs" -Pattern "\.Setup\(m => m\.GetAllAsync" -Recurse
For each match, check the parameter count against the new interface.
Add It.IsAny<T>() for each new parameter (unless the test needs to assert the specific value).
Run the specific test class immediately to verify the fix before moving on:
dotnet test .\src\ --filter "FullyQualifiedName~SchedulesControllerTests" --no-build --verbosity normal
Run the full test suite before pushing:
dotnet test .\src\ --no-build --verbosity normal --filter "FullyQualifiedName!~SyndicationFeedReader"
When a controller has an IsSiteAdministrator() branch that calls the unfiltered overload, both setups must exist simultaneously in the same test class:
// Setup for non-admin tests (owner-filtered overload):
_scheduledItemManagerMock
.Setup(m => m.GetAllAsync(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync(pagedResult);
// Setup for admin tests (unfiltered overload):
_scheduledItemManagerMock
.Setup(m => m.GetAllAsync(It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync(pagedResult);
Then in admin-bypass tests, verify that the unfiltered overload fires and the owner-filtered one does not:
_scheduledItemManagerMock.Verify(
m => m.GetAllAsync(It.IsAny<int>(), It.IsAny<int>()),
Times.Once);
_scheduledItemManagerMock.Verify(
m => m.GetAllAsync(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()),
Times.Never);
This is the same pattern used in the security-test-checklist SKILL for admin bypass verification — the two skills are complementary.
// WRONG — setup for old overload; controller calls new overload → null result
_managerMock.Setup(m => m.GetAllAsync(It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync(result);
// WRONG — if the test OID ever changes, this setup silently stops matching
_managerMock.Setup(m => m.GetAllAsync("hardcoded-oid", It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync(result);
// WRONG workflow: update setup → push → find out CI failed
// RIGHT workflow: update setup → dotnet test --filter "FullyQualifiedName~XTests" → green → push
Moq setups are resolved at runtime, not compile time. A setup for the wrong overload compiles without warning. Always run the test after a setup change.
GetAllAsync(ownerOid, page, size) alongside the existing GetAllAsync(page, size) on all four manager interfacesIScheduledItemManager, IEngagementManager, ITalkManager, IMessageTemplateManager*ControllerTests.cs files in src/JosephGuadagno.Broadcasting.Api.Tests/Controllers/まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Standard collaboration patterns for all squad agents — worktree awareness, decisions, cross-agent communication
日本語の概要は準備中です。原文の説明を表示しています。
Shared hard rules enforced across all squad agents
日本語の概要は準備中です。原文の説明を表示しています。
Comprehensive AI prompt engineering safety review and improvement prompt. Analyzes prompts for safety, bias, security vulnerabilities, and effectiveness while providing detailed improvement recommendations with extensive frameworks, testing methodologies, and educational content.
日本語の概要は準備中です。原文の説明を表示しています。
Migrate API controller scope checks to framework-enforced authorization policies while preserving ownership checks.
日本語の概要は準備中です。原文の説明を表示しています。
Review API DTO implementations for contract/domain separation, mapping patterns, and REST compliance
日本語の概要は準備中です。原文の説明を表示しています。
Register shared claims transformation and hierarchical role policies in the API without breaking existing scope enforcement.
日本語の概要は準備中です。原文の説明を表示しています。