mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
948a9a52ef
* Add exp-mock-usage-analysis skill and evaluation New experimental skill that detects unused, redundant, or unnecessary mocks in .NET test suites. Covers Moq, NSubstitute, FakeItEasy, and manual test doubles. - SKILL.md with 6-step workflow: gather code, classify mocks, detect anti-patterns, incorporate runtime data, generate recommendations, report findings - eval.yaml with 10 scenarios: trivial type mocking, unused setups, redundant configs, stable utility mocks, well-placed mocks (no false positives), NSubstitute support, FakeItEasy support, setup sprawl, and 2 non-activation boundary tests - Fixture files for each scenario with production + test code - CODEOWNERS entry for @dotnet/dotnet-testing * Remove no-headroom scenarios, increase NSubstitute timeout Remove 4 scenarios where baseline already scores 5.0/5 (model handles these well without skill guidance): - Detect mocking of DTOs, records, and enums - Recognize well-placed mocks without inventing false positives - Decline when asked about non-mock test anti-patterns - Decline mock framework migration request Increase NSubstitute scenario timeout from 120s to 180s to avoid baseline timeouts impacting scoring. Remaining 6 scenarios all show positive skill impact (3→5, 3→4, etc). * Document areas the model handles well without the skill Add a subsection under 'When Not to Use' listing mock-related tasks where the base model already scores 5.0/5 without skill guidance: - Mocking trivial types (DTOs, records, enums) - Recognizing well-placed mocks (no false positives) * Fix md * Improve
213 lines
12 KiB
YAML
213 lines
12 KiB
YAML
scenarios:
|
|
# ============================================================
|
|
# Scenarios removed (baseline already scores 5.0/5 — no headroom):
|
|
# - "Detect mocking of DTOs, records, and enums": Model already
|
|
# identifies trivial-type mocking without skill guidance.
|
|
# - "Recognize well-placed mocks without inventing false positives":
|
|
# Model already avoids false positives on well-structured tests.
|
|
# ============================================================
|
|
|
|
# ============================================================
|
|
# Scenario 1: Unused mock setups
|
|
# ============================================================
|
|
- name: "Detect unused and unreachable mock setups"
|
|
prompt: |
|
|
I've been told our tests have a lot of dead mock setup code — setups for
|
|
mocks that never get invoked during the test. Can you audit the mock usage
|
|
in these tests against the production code and identify which mock setups
|
|
are actually necessary and which are dead code?
|
|
|
|
Test file: InventoryServiceTests.cs
|
|
Production file: InventoryService.cs
|
|
setup:
|
|
files:
|
|
- path: "InventoryServiceTests.cs"
|
|
source: "unused-setups/InventoryServiceTests.cs"
|
|
- path: "InventoryService.cs"
|
|
source: "unused-setups/InventoryService.cs"
|
|
assertions:
|
|
- type: "output_matches"
|
|
pattern: "(unused|dead|unreachable|never.*call|not.*invoke|unnecessary.*setup)"
|
|
- type: "output_matches"
|
|
pattern: "(GetLowStockProducts|low.?stock)"
|
|
- type: "output_matches"
|
|
pattern: "(Restock_ZeroQuantity|throws.*before|argument.*validation|early.*return)"
|
|
- type: "exit_success"
|
|
rubric:
|
|
- "Identified that GetLowStockProducts is set up but never called by any method under test"
|
|
- "Identified that in Reserve_InsufficientStock, the UpdateStock/AuditLogger/EventBus setups are unreachable because the method returns early when stock is insufficient"
|
|
- "Identified that in Restock_ZeroQuantity, all mock setups are unnecessary because the method throws ArgumentOutOfRangeException before using any dependency"
|
|
- "Identified that Reserve_VerifiesExactCallSequence has over-verification — it tests exact call sequence rather than behavioral outcomes"
|
|
- "Provided concrete recommendations on which setup lines to remove from each test method"
|
|
timeout: 180
|
|
|
|
# ============================================================
|
|
# Scenario 3: Redundant mock configurations across tests
|
|
# ============================================================
|
|
- name: "Detect redundant mock configurations duplicated across tests"
|
|
prompt: |
|
|
Every time I add a new test to this file, I have to copy-paste the same
|
|
mock setup — creating Mock<IPaymentGateway>, Mock<IInvoiceStore>, and
|
|
Mock<ICustomerLookup> over and over. Can you audit the mock usage and
|
|
suggest a better way to organize these redundant mocks?
|
|
|
|
Test file: BillingServiceTests.cs
|
|
Production file: BillingService.cs
|
|
setup:
|
|
files:
|
|
- path: "BillingServiceTests.cs"
|
|
source: "redundant-mocks/BillingServiceTests.cs"
|
|
- path: "BillingService.cs"
|
|
source: "redundant-mocks/BillingService.cs"
|
|
assertions:
|
|
- type: "output_matches"
|
|
pattern: "(redundant|duplicat|repeated|identical.*setup|copy|shared|extract|helper|fixture)"
|
|
- type: "output_matches"
|
|
pattern: "(ChargeCustomer_CustomerNotFound|gateway.*setup.*never.*reach|unreachable)"
|
|
- type: "exit_success"
|
|
rubric:
|
|
- "Identified that all five tests create identical mock boilerplate (three Mock<> instantiations) independently"
|
|
- "Recommended extracting shared mock creation into a test fixture, helper method, or TestInitialize"
|
|
- "Identified that ChargeCustomer_CustomerNotFound sets up gateway and invoice store mocks that are unreachable after the customer lookup returns null"
|
|
- "Identified that ChargeCustomer_VerifiesGatewayCalledWithCorrectAmount only verifies interactions without asserting on the return value"
|
|
- "Provided a concrete before/after example showing how to reduce the duplication"
|
|
timeout: 180
|
|
|
|
# ============================================================
|
|
# Scenario 4: Mocking stable utilities (ILogger, IOptions)
|
|
# ============================================================
|
|
- name: "Detect mocking of stable framework types"
|
|
prompt: |
|
|
I read that mocking ILogger and IOptions is considered a bad practice
|
|
in most cases. Can you check our authentication tests and tell me if our
|
|
mock setup is appropriate or if we should use real implementations?
|
|
|
|
Test file: AuthenticationServiceTests.cs
|
|
Production file: AuthenticationService.cs
|
|
setup:
|
|
files:
|
|
- path: "AuthenticationServiceTests.cs"
|
|
source: "stable-utility-mocks/AuthenticationServiceTests.cs"
|
|
- path: "AuthenticationService.cs"
|
|
source: "stable-utility-mocks/AuthenticationService.cs"
|
|
assertions:
|
|
- type: "output_matches"
|
|
pattern: "(NullLogger|Options\\.Create|real.*implementation|replace.*mock|unnecessary.*mock.*ILogger|unnecessary.*mock.*IOptions)"
|
|
- type: "output_matches"
|
|
pattern: "(IUserStore|IPasswordHasher).*(keep|appropriate|correct|valid)"
|
|
- type: "exit_success"
|
|
rubric:
|
|
- "Identified that Mock<ILogger<AuthenticationService>> should be replaced with NullLogger<AuthenticationService>.Instance since log output is never asserted"
|
|
- "Identified that Mock<IOptions<UserManagerOptions>> should be replaced with Options.Create(new UserManagerOptions { ... }) since it is a simple value wrapper"
|
|
- "Correctly noted that IUserStore and IPasswordHasher mocks are appropriate because they represent external data access and security boundaries"
|
|
- "Identified that Login_LockedAccount sets up a password hasher mock that is never reached because the locked check short-circuits"
|
|
timeout: 120
|
|
|
|
# ============================================================
|
|
# Scenarios removed (baseline already scores 5.0/5 — no headroom):
|
|
# - "Recognize well-placed mocks without inventing false positives":
|
|
# Model already avoids false positives on well-structured tests.
|
|
# ============================================================
|
|
|
|
# ============================================================
|
|
# Scenario 4: NSubstitute framework support
|
|
# ============================================================
|
|
- name: "Analyze mock usage in NSubstitute tests"
|
|
prompt: |
|
|
We use NSubstitute in this part of the codebase. I suspect some of our
|
|
substitute setups are unnecessary. Can you analyze these tests and the
|
|
service code to find any wasted mock configuration?
|
|
|
|
Test file: ShippingServiceTests.cs
|
|
Production file: ShippingService.cs
|
|
setup:
|
|
files:
|
|
- path: "ShippingServiceTests.cs"
|
|
source: "nsubstitute-mocks/ShippingServiceTests.cs"
|
|
- path: "ShippingService.cs"
|
|
source: "nsubstitute-mocks/ShippingService.cs"
|
|
assertions:
|
|
- type: "output_matches"
|
|
pattern: "(unused|unnecessary|never.*call|not.*invoke|GetTracking|CreateShipment.*GetQuote|CalculateRate.*Ship)"
|
|
- type: "output_matches"
|
|
pattern: "(Ship_OnlyVerifiesInteractions|Received|interaction.*only|no.*assert.*result)"
|
|
- type: "exit_success"
|
|
rubric:
|
|
- "Identified unused NSubstitute setups — GetTracking and CreateShipment are set up in GetQuote tests but never called by that method"
|
|
- "Identified that GetQuote_InvalidOrigin sets up Normalize, CalculateRate, CreateShipment, and GetTracking — all unreachable after the address validation fails"
|
|
- "Identified that Ship_OnlyVerifiesInteractions only checks Received() calls without asserting on the returned shipment ID"
|
|
- "Used correct NSubstitute terminology (Substitute.For, .Returns, .Received) rather than Moq terms"
|
|
timeout: 180
|
|
|
|
# ============================================================
|
|
# Scenario 7: FakeItEasy framework support
|
|
# ============================================================
|
|
- name: "Analyze mock usage in FakeItEasy tests"
|
|
prompt: |
|
|
Our notification tests use FakeItEasy. We're seeing some fake setups that
|
|
might not be needed. Can you audit the mock usage and identify which
|
|
A.CallTo configurations are unnecessary? Don't modify any files — just
|
|
explain your findings.
|
|
|
|
Test file: NotificationDispatcherTests.cs
|
|
Production file: NotificationDispatcher.cs
|
|
setup:
|
|
files:
|
|
- path: "NotificationDispatcherTests.cs"
|
|
source: "fakeiteasy-mocks/NotificationDispatcherTests.cs"
|
|
- path: "NotificationDispatcher.cs"
|
|
source: "fakeiteasy-mocks/NotificationDispatcher.cs"
|
|
reject_tools: ["bash", "edit"]
|
|
assertions:
|
|
- type: "output_matches"
|
|
pattern: "(unused|unnecessary|never.*call|not.*invoke|push.*sms|sms.*push|wrong.*channel)"
|
|
- type: "exit_success"
|
|
rubric:
|
|
- "Identified that the SMS test sets up the push notifier unnecessarily, and the push test sets up the SMS sender unnecessarily"
|
|
- "Identified that in Dispatch_UnknownChannel, both smsSender and pushNotifier setups are unreachable because the switch throws before reaching them"
|
|
- "Used correct FakeItEasy terminology (A.Fake, A.CallTo, MustHaveHappened) rather than Moq or NSubstitute terms"
|
|
- "Acknowledged that the template engine mock is correctly used across all tests since rendering is always called before dispatch"
|
|
timeout: 120
|
|
|
|
# ============================================================
|
|
# Scenario 8: Setup sprawl (excessive mock configuration)
|
|
# ============================================================
|
|
- name: "Detect excessive mock configuration sprawl"
|
|
prompt: |
|
|
Each test in our report generator file is over 40 lines long and most of
|
|
it is just setting up mocks. The mock configuration completely dominates
|
|
the test logic. Can you audit the mock usage and tell me which setups
|
|
are unnecessary?
|
|
|
|
Test file: ReportGeneratorTests.cs
|
|
Production file: ReportGenerator.cs
|
|
setup:
|
|
files:
|
|
- path: "ReportGeneratorTests.cs"
|
|
source: "setup-sprawl/ReportGeneratorTests.cs"
|
|
- path: "ReportGenerator.cs"
|
|
source: "setup-sprawl/ReportGenerator.cs"
|
|
assertions:
|
|
- type: "output_matches"
|
|
pattern: "(sprawl|excessive|too many|bloat|overwhelm|unreadable|dominant|setup.*lines)"
|
|
- type: "output_matches"
|
|
pattern: "(GetCustomers|GetProducts|GetExchangeRate|FormatAsPdf|FormatAsCsv|GetLatest|cache).*(unused|never|unnecessary|not.*call)"
|
|
- type: "output_matches"
|
|
pattern: "(extract|helper|builder|fixture|shared|TestInitialize|refactor)"
|
|
- type: "exit_success"
|
|
rubric:
|
|
- "Identified that the majority of setup lines are unused — GetCustomers, GetProducts, GetExchangeRate, cache operations, and non-matching formatters are never called by GenerateSalesReport"
|
|
- "Identified that the two tests duplicate nearly identical 30+ line mock configurations"
|
|
- "Recommended extracting shared setup into a helper, builder, or TestInitialize to reduce duplication"
|
|
- "Suggested that mocking ILogger and ICacheService could be replaced with NullLogger and a no-op cache if never asserted"
|
|
- "Noted that the class may have too many dependencies, which could indicate the SUT has too many responsibilities"
|
|
timeout: 180
|
|
|
|
# ============================================================
|
|
# Non-activation scenarios removed (baseline already scores 5.0/5):
|
|
# - "Decline when asked about non-mock test anti-patterns":
|
|
# Model already handles non-mock test reviews without skill.
|
|
# - "Decline mock framework migration request":
|
|
# Model already handles migration requests without skill.
|
|
# ============================================================
|