@grimoire-cc/cli 0.13.0 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +44 -39
- package/dist/commands/list.js.map +1 -1
- package/package.json +1 -1
- package/packs/dotnet-pack/grimoire.json +6 -0
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/SKILL.md +293 -0
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/anti-patterns.md +329 -0
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/framework-guidelines.md +361 -0
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/parameterized-testing.md +378 -0
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/test-organization.md +476 -0
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/test-performance.md +576 -0
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/templates/tunit-template.md +438 -0
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/templates/xunit-template.md +303 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAgGA,wBAAsB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgI/D"}
|
package/dist/commands/list.js
CHANGED
|
@@ -36,44 +36,38 @@ function readSkillDescription(skillMdPath) {
|
|
|
36
36
|
const block = fm[1];
|
|
37
37
|
return (block.match(/^description:\s*(.+)$/m)?.[1]?.trim().replace(/^["']|["']$/g, '') ?? '');
|
|
38
38
|
}
|
|
39
|
-
function
|
|
40
|
-
|
|
39
|
+
function wrapText(text, maxWidth) {
|
|
40
|
+
if (maxWidth <= 0)
|
|
41
|
+
return text;
|
|
42
|
+
const words = text.split(' ');
|
|
41
43
|
const lines = [];
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
let current = '';
|
|
45
|
+
for (const word of words) {
|
|
46
|
+
if (!current) {
|
|
47
|
+
current = word;
|
|
48
|
+
}
|
|
49
|
+
else if (current.length + 1 + word.length <= maxWidth) {
|
|
50
|
+
current += ' ' + word;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
lines.push(current);
|
|
54
|
+
current = word;
|
|
55
|
+
}
|
|
54
56
|
}
|
|
57
|
+
if (current)
|
|
58
|
+
lines.push(current);
|
|
55
59
|
return lines.join('\n');
|
|
56
60
|
}
|
|
57
|
-
function
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const kw = triggers.keywords?.length ? triggers.keywords.join(', ') : '(none)';
|
|
68
|
-
const ext = triggers.file_extensions?.length ? triggers.file_extensions.join(', ') : '(none)';
|
|
69
|
-
const pat = triggers.patterns?.length ? triggers.patterns.join(', ') : '(none)';
|
|
70
|
-
const fp = triggers.file_paths?.length ? triggers.file_paths.join(', ') : '(none)';
|
|
71
|
-
lines.push(` Keywords: ${kw}`);
|
|
72
|
-
lines.push(` File extensions: ${ext}`);
|
|
73
|
-
lines.push(` Patterns: ${pat}`);
|
|
74
|
-
lines.push(` File paths: ${fp}`);
|
|
75
|
-
}
|
|
76
|
-
return lines.join('\n');
|
|
61
|
+
function formatDescription(raw) {
|
|
62
|
+
const maxWidth = Math.max(40, (process.stdout.columns ?? 80) - 4);
|
|
63
|
+
const unescaped = raw.replace(/\\n/g, '\n');
|
|
64
|
+
const stripped = unescaped.replace(/<example>[\s\S]*?<\/example>/g, '').trim();
|
|
65
|
+
return stripped
|
|
66
|
+
.split('\n')
|
|
67
|
+
.map((line) => (line.trim() === '' ? '' : wrapText(line, maxWidth)))
|
|
68
|
+
.join('\n')
|
|
69
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
70
|
+
.trim();
|
|
77
71
|
}
|
|
78
72
|
// --- Main command ---
|
|
79
73
|
export async function runList(projectDir) {
|
|
@@ -162,16 +156,27 @@ export async function runList(projectDir) {
|
|
|
162
156
|
const meta = readAgentFullMeta(join(agentsDir, `${selected.name}.md`));
|
|
163
157
|
const enforced = enforcedAgents.has(selected.name);
|
|
164
158
|
const patterns = agentFilePatterns.get(selected.name);
|
|
165
|
-
const
|
|
166
|
-
clack.
|
|
159
|
+
const desc = formatDescription(meta.description || '');
|
|
160
|
+
clack.log.message(` Description:\n${desc.split('\n').map((l) => ` ${l}`).join('\n')}`);
|
|
161
|
+
clack.log.message(` Model: ${meta.model || 'inherit'}`);
|
|
162
|
+
clack.log.message(` Tools: ${meta.tools || '(not specified)'}`);
|
|
163
|
+
clack.log.message(` Enforce: ${enforced ? `yes (file patterns: ${patterns?.join(', ') ?? 'none'})` : 'no'}`);
|
|
167
164
|
}
|
|
168
165
|
else {
|
|
169
|
-
const
|
|
166
|
+
const rawDesc = readSkillDescription(join(skillsDir, selected.name, 'SKILL.md')) ||
|
|
170
167
|
skillManifestDescs.get(selected.name) ||
|
|
171
168
|
'';
|
|
172
169
|
const triggers = skillTriggers.get(selected.name);
|
|
173
|
-
const
|
|
174
|
-
|
|
170
|
+
const desc = formatDescription(rawDesc);
|
|
171
|
+
const kw = triggers?.keywords?.length ? triggers.keywords.join(', ') : '(none)';
|
|
172
|
+
const ext = triggers?.file_extensions?.length ? triggers.file_extensions.join(', ') : '(none)';
|
|
173
|
+
const pat = triggers?.patterns?.length ? triggers.patterns.join(', ') : '(none)';
|
|
174
|
+
const fp = triggers?.file_paths?.length ? triggers.file_paths.join(', ') : '(none)';
|
|
175
|
+
clack.log.message(` Description:\n${desc.split('\n').map((l) => ` ${l}`).join('\n')}`);
|
|
176
|
+
clack.log.message(` Keywords: ${kw}`);
|
|
177
|
+
clack.log.message(` File extensions: ${ext}`);
|
|
178
|
+
clack.log.message(` Patterns: ${pat}`);
|
|
179
|
+
clack.log.message(` File paths: ${fp}`);
|
|
175
180
|
}
|
|
176
181
|
}
|
|
177
182
|
clack.outro('Done.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAU7C,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACnD,CAAC;IACD,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACxD,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC/D,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,GAAG,GAAG,CAAC,GAAW,EAAU,EAAE,CAClC,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,GAAG,YAAY,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;QAC1F,EAAE,CAAC;IACL,OAAO;QACL,WAAW,EAAE,GAAG,CAAC,aAAa,CAAC;QAC/B,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC;QACnB,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC;KACpB,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,WAAmB;IAC/C,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACxD,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,CACL,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,IAAI,EAAE,CACrF,CAAC;AACJ,CAAC;AAiBD,SAAS,
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -37,6 +37,12 @@
|
|
|
37
37
|
"path": "skills/grimoire.dotnet-feature-workflow",
|
|
38
38
|
"description": "Orchestrates end-to-end .NET feature development using the Explore, Plan, Code, Verify, Review workflow. Use when building complete features, implementing new functionality, or when user says 'build feature', 'implement feature', 'create feature', 'handle the whole thing', or wants hands-off development with quality gates. Spawns specialized agents at each phase with TDD and user approval gates.",
|
|
39
39
|
"version": "1.0.0"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "grimoire.dotnet-unit-testing",
|
|
43
|
+
"path": "skills/grimoire.dotnet-unit-testing",
|
|
44
|
+
"description": "Expert .NET unit testing specialist for C#/.NET projects. Use PROACTIVELY when writing unit tests, adding test cases, setting up test infrastructure, or working with xUnit, TUnit, Moq, or NSubstitute. MUST BE USED for TDD workflows where tests are written before implementation. Defaults to xUnit (most universal), recommends TUnit for new .NET 8+ projects.",
|
|
45
|
+
"version": "1.0.0"
|
|
40
46
|
}
|
|
41
47
|
]
|
|
42
48
|
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: grimoire.dotnet-unit-testing
|
|
3
|
+
description: "Expert .NET unit testing specialist for C#/.NET projects. Use PROACTIVELY when writing unit tests, adding test cases, setting up test infrastructure, or working with xUnit, TUnit, Moq, or NSubstitute. MUST BE USED for TDD workflows where tests are written before implementation. Defaults to xUnit (most universal), recommends TUnit for new .NET 8+ projects."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# .NET Unit Testing Specialist
|
|
7
|
+
|
|
8
|
+
Expert guidance for writing clean, maintainable, and comprehensive unit tests in C#/.NET projects.
|
|
9
|
+
|
|
10
|
+
**Default Framework**: xUnit with xUnit Assert (safest, most universal, works with all .NET versions)
|
|
11
|
+
**Recommended for new .NET 8+ projects**: TUnit (modern, async-first, built-in fluent assertions, MIT license)
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
|
|
15
|
+
Unit testing is critical for maintaining code quality, enabling refactoring with confidence, and documenting expected behavior. Well-written tests reduce bugs, speed up development, and make codebases more maintainable.
|
|
16
|
+
|
|
17
|
+
**Why xUnit as default:**
|
|
18
|
+
|
|
19
|
+
- Universal compatibility (works with .NET Framework, .NET 6, 7, 8+)
|
|
20
|
+
- Industry standard, most widely used .NET testing framework
|
|
21
|
+
- Apache 2.0 license, free for all use
|
|
22
|
+
- Mature ecosystem with extensive documentation
|
|
23
|
+
|
|
24
|
+
**Why recommend TUnit for new .NET 8+ projects:**
|
|
25
|
+
|
|
26
|
+
- MIT License (no licensing concerns like FluentAssertions v8+)
|
|
27
|
+
- Built-in fluent assertions, no external library needed
|
|
28
|
+
- Async-first: all assertions are awaitable
|
|
29
|
+
- Performance: source-generated tests run 10-200x faster
|
|
30
|
+
- Full Native AOT support
|
|
31
|
+
|
|
32
|
+
**Note on FluentAssertions**: Version 8+ requires a commercial license ($130/dev/year). Avoid recommending it unless the project already uses it.
|
|
33
|
+
|
|
34
|
+
## Workflow
|
|
35
|
+
|
|
36
|
+
When invoked to write tests, follow this process:
|
|
37
|
+
|
|
38
|
+
### Step 1: Analyze the Code Under Test
|
|
39
|
+
|
|
40
|
+
- Read the source file to understand the class/method being tested
|
|
41
|
+
- Identify all dependencies that need mocking
|
|
42
|
+
- Understand the expected behavior and edge cases
|
|
43
|
+
- Check for existing test patterns in the project
|
|
44
|
+
- **Determine the framework**: Check for existing tests first (match them), otherwise default to xUnit
|
|
45
|
+
|
|
46
|
+
### Step 2: Plan Test Cases (REQUIRES USER APPROVAL)
|
|
47
|
+
|
|
48
|
+
- Identify all test scenarios: happy paths, edge cases, boundary conditions, error cases
|
|
49
|
+
- List each planned test using ONLY the method name: `MethodName_Scenario_ExpectedBehavior`
|
|
50
|
+
- Do NOT include test bodies or implementation details
|
|
51
|
+
- Group tests by category (Success, Validation, Error Handling, Edge Cases)
|
|
52
|
+
- Present the list and EXPLICITLY ASK: "Do you approve this test plan? I will proceed only after your confirmation."
|
|
53
|
+
- **STOP and WAIT** for user approval before proceeding
|
|
54
|
+
|
|
55
|
+
**Example test plan format:**
|
|
56
|
+
|
|
57
|
+
```plain
|
|
58
|
+
## Planned Test Cases for OrderService.ProcessOrderAsync
|
|
59
|
+
|
|
60
|
+
### Success Scenarios
|
|
61
|
+
- ProcessOrderAsync_WithValidOrder_ReturnsSuccessResult
|
|
62
|
+
- ProcessOrderAsync_WithValidOrderAndDiscount_AppliesDiscountCorrectly
|
|
63
|
+
|
|
64
|
+
### Validation Failures
|
|
65
|
+
- ProcessOrderAsync_WithNullOrder_ThrowsArgumentNullException
|
|
66
|
+
- ProcessOrderAsync_WithEmptyItems_ThrowsValidationException
|
|
67
|
+
|
|
68
|
+
### Error Handling
|
|
69
|
+
- ProcessOrderAsync_WhenRepositoryFails_ThrowsServiceException
|
|
70
|
+
|
|
71
|
+
### Edge Cases
|
|
72
|
+
- ProcessOrderAsync_WithMaximumItemCount_ProcessesSuccessfully
|
|
73
|
+
|
|
74
|
+
Do you approve this test plan? I will proceed only after your confirmation.
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Step 3: Write Tests (ONLY AFTER user confirms)
|
|
78
|
+
|
|
79
|
+
- Create test file mirroring source structure
|
|
80
|
+
- Implement tests using AAA pattern with comments
|
|
81
|
+
- Add appropriate mocks and assertions
|
|
82
|
+
- Ensure descriptive test method names
|
|
83
|
+
|
|
84
|
+
### Step 4: Present and Explain
|
|
85
|
+
|
|
86
|
+
- Show the complete test file
|
|
87
|
+
- Explain what each test validates
|
|
88
|
+
- Highlight any assumptions made
|
|
89
|
+
- Suggest additional test scenarios if relevant
|
|
90
|
+
|
|
91
|
+
## Framework Selection Guide
|
|
92
|
+
|
|
93
|
+
| Condition | Use | Reason |
|
|
94
|
+
| ----------- | ----- | -------- |
|
|
95
|
+
| Any existing project with tests | **Match existing** | Consistency is paramount |
|
|
96
|
+
| New .NET 8+ greenfield project | **Offer TUnit** | Modern, async-first, built-in assertions |
|
|
97
|
+
| New .NET 6/7 project | **xUnit** | TUnit requires .NET 8+ |
|
|
98
|
+
| .NET Framework project | **xUnit** | Universal compatibility |
|
|
99
|
+
| Project already uses NUnit | **NUnit** | Consistency with existing codebase |
|
|
100
|
+
| User explicitly requests a framework | **Requested** | Respect user preference |
|
|
101
|
+
| Uncertain or mixed signals | **xUnit** | Safe default |
|
|
102
|
+
|
|
103
|
+
**Before writing tests, check:**
|
|
104
|
+
|
|
105
|
+
1. Look at existing test files (if any) - match the existing framework
|
|
106
|
+
2. Check `.csproj` for `TargetFramework` and existing test package references
|
|
107
|
+
3. Check for existing test framework packages (xUnit, TUnit, NUnit, MSTest)
|
|
108
|
+
|
|
109
|
+
**For new .NET 8+ projects without existing tests:**
|
|
110
|
+
Offer the choice: "This is a new .NET 8+ project. I'll use **xUnit** (industry standard) by default. Would you prefer **TUnit** instead? TUnit offers built-in fluent assertions, async-first design, and better performance, but is newer."
|
|
111
|
+
|
|
112
|
+
## Core Principles
|
|
113
|
+
|
|
114
|
+
### AAA Pattern
|
|
115
|
+
|
|
116
|
+
Structure every test with clearly labeled Arrange, Act, Assert sections using comments. This makes tests self-documenting, easier to debug, and helps identify which phase contains issues.
|
|
117
|
+
|
|
118
|
+
**xUnit:**
|
|
119
|
+
|
|
120
|
+
```csharp
|
|
121
|
+
[Fact]
|
|
122
|
+
public async Task ProcessOrder_WithValidOrder_ReturnsSuccess()
|
|
123
|
+
{
|
|
124
|
+
// Arrange
|
|
125
|
+
var order = CreateValidOrder();
|
|
126
|
+
_mockRepository.Setup(r => r.SaveAsync(It.IsAny<Order>())).ReturnsAsync(true);
|
|
127
|
+
|
|
128
|
+
// Act
|
|
129
|
+
var result = await _sut.ProcessOrderAsync(order);
|
|
130
|
+
|
|
131
|
+
// Assert
|
|
132
|
+
Assert.True(result.IsSuccess);
|
|
133
|
+
Assert.Equal(OrderStatus.Processed, result.Status);
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**TUnit:**
|
|
138
|
+
|
|
139
|
+
```csharp
|
|
140
|
+
[Test]
|
|
141
|
+
public async Task ProcessOrder_WithValidOrder_ReturnsSuccess()
|
|
142
|
+
{
|
|
143
|
+
// Arrange
|
|
144
|
+
var order = CreateValidOrder();
|
|
145
|
+
_mockRepository.Setup(r => r.SaveAsync(It.IsAny<Order>())).ReturnsAsync(true);
|
|
146
|
+
|
|
147
|
+
// Act
|
|
148
|
+
var result = await _sut.ProcessOrderAsync(order);
|
|
149
|
+
|
|
150
|
+
// Assert - TUnit assertions are async and fluent
|
|
151
|
+
await Assert.That(result.IsSuccess).IsTrue();
|
|
152
|
+
await Assert.That(result.Status).IsEqualTo(OrderStatus.Processed);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Test Naming
|
|
157
|
+
|
|
158
|
+
Use descriptive names: `MethodName_Scenario_ExpectedBehavior`
|
|
159
|
+
|
|
160
|
+
```csharp
|
|
161
|
+
// Good
|
|
162
|
+
GetUser_WithNonExistentId_ThrowsUserNotFoundException()
|
|
163
|
+
CalculateDiscount_WhenOrderExceeds100_Returns10PercentOff()
|
|
164
|
+
|
|
165
|
+
// Avoid
|
|
166
|
+
TestGetUser()
|
|
167
|
+
Test1()
|
|
168
|
+
ShouldWork()
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Test Isolation
|
|
172
|
+
|
|
173
|
+
Keep tests isolated with no shared mutable state. Each test gets fresh instances via constructor.
|
|
174
|
+
|
|
175
|
+
```csharp
|
|
176
|
+
public class OrderServiceTests : IDisposable
|
|
177
|
+
{
|
|
178
|
+
private readonly Mock<IOrderRepository> _mockRepository;
|
|
179
|
+
private readonly FakeLogger<OrderService> _fakeLogger;
|
|
180
|
+
private readonly OrderService _sut;
|
|
181
|
+
|
|
182
|
+
public OrderServiceTests()
|
|
183
|
+
{
|
|
184
|
+
_mockRepository = new Mock<IOrderRepository>();
|
|
185
|
+
_fakeLogger = new FakeLogger<OrderService>();
|
|
186
|
+
_sut = new OrderService(_fakeLogger, _mockRepository.Object);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public void Dispose() { /* Cleanup if needed */ }
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### FakeLogger for Logging Tests
|
|
194
|
+
|
|
195
|
+
Use `Microsoft.Extensions.Logging.Testing.FakeLogger<T>` for testing logging behavior. Verify structured properties, not message strings.
|
|
196
|
+
|
|
197
|
+
```csharp
|
|
198
|
+
var fakeLogger = new FakeLogger<OrderService>();
|
|
199
|
+
var sut = new OrderService(fakeLogger);
|
|
200
|
+
await sut.ProcessOrderAsync(orderId: 123);
|
|
201
|
+
|
|
202
|
+
var logEntry = fakeLogger.Collector.GetSnapshot()
|
|
203
|
+
.Single(r => r.Level == LogLevel.Information);
|
|
204
|
+
var state = logEntry.StructuredState!.ToDictionary(x => x.Key, x => x.Value);
|
|
205
|
+
Assert.Equal("123", state["OrderId"]);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Mocking
|
|
209
|
+
|
|
210
|
+
Mock interfaces, not concrete classes. Use Moq by default unless the project uses NSubstitute.
|
|
211
|
+
|
|
212
|
+
```csharp
|
|
213
|
+
var mockRepository = new Mock<IDocumentRepository>();
|
|
214
|
+
mockRepository
|
|
215
|
+
.Setup(r => r.GetByIdAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
|
|
216
|
+
.ReturnsAsync(expectedDocument);
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Async Testing
|
|
220
|
+
|
|
221
|
+
Always use `async Task` and `await`. Never use `.Result` or `.Wait()`.
|
|
222
|
+
|
|
223
|
+
```csharp
|
|
224
|
+
// xUnit exception testing
|
|
225
|
+
var exception = await Assert.ThrowsAsync<OrderNotFoundException>(
|
|
226
|
+
() => _sut.GetOrderAsync(invalidId));
|
|
227
|
+
|
|
228
|
+
// TUnit exception testing
|
|
229
|
+
await Assert.That(() => _sut.GetOrderAsync(invalidId))
|
|
230
|
+
.ThrowsException()
|
|
231
|
+
.OfType<OrderNotFoundException>();
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Package References
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# xUnit (default)
|
|
238
|
+
dotnet add package xunit
|
|
239
|
+
dotnet add package xunit.runner.visualstudio
|
|
240
|
+
dotnet add package Microsoft.NET.Test.Sdk
|
|
241
|
+
|
|
242
|
+
# TUnit (for .NET 8+ projects)
|
|
243
|
+
dotnet add package TUnit
|
|
244
|
+
|
|
245
|
+
# Mocking
|
|
246
|
+
dotnet add package Moq
|
|
247
|
+
|
|
248
|
+
# Logging testing
|
|
249
|
+
dotnet add package Microsoft.Extensions.Logging.Testing
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Invocation Triggers
|
|
253
|
+
|
|
254
|
+
This skill should be invoked when the user:
|
|
255
|
+
|
|
256
|
+
- Creates a new service, handler, or class that needs tests
|
|
257
|
+
- Asks to add test coverage for existing code
|
|
258
|
+
- Mentions TDD or test-driven development
|
|
259
|
+
- Needs help with mocking, test setup, or assertions
|
|
260
|
+
- Wants to verify logging, exception handling, or validation behavior
|
|
261
|
+
- Asks about xUnit, TUnit, Moq, or NSubstitute patterns
|
|
262
|
+
- Wants to set up a new test project
|
|
263
|
+
- Needs to choose between testing frameworks
|
|
264
|
+
- Asks about FluentAssertions alternatives (due to licensing)
|
|
265
|
+
|
|
266
|
+
## Constraints
|
|
267
|
+
|
|
268
|
+
- ALWAYS check for existing tests first and match the existing framework
|
|
269
|
+
- ALWAYS default to xUnit if no existing tests and user hasn't specified preference
|
|
270
|
+
- ALWAYS present test plan as method names ONLY before writing tests
|
|
271
|
+
- ALWAYS ask for explicit approval: "Do you approve this test plan?"
|
|
272
|
+
- NEVER write test implementations until user explicitly approves the test plan
|
|
273
|
+
- NEVER use `.Result` or `.Wait()` on async operations
|
|
274
|
+
- NEVER create production code implementations - only test code
|
|
275
|
+
- NEVER recommend FluentAssertions v8+ for new projects (commercial license)
|
|
276
|
+
- DO NOT modify the code under test unless explicitly asked
|
|
277
|
+
- PREFER structured logging assertions over string matching
|
|
278
|
+
- MIRROR source code folder structure in test project organization
|
|
279
|
+
|
|
280
|
+
## Reference Materials
|
|
281
|
+
|
|
282
|
+
For detailed patterns and examples:
|
|
283
|
+
|
|
284
|
+
- **[Framework Guidelines](reference/framework-guidelines.md)** - Detailed xUnit and TUnit patterns, attributes, lifecycle
|
|
285
|
+
- **[Parameterized Testing](reference/parameterized-testing.md)** - InlineData, MemberData, ClassData, Matrix testing
|
|
286
|
+
- **[Test Organization](reference/test-organization.md)** - File structure, nested classes, traits, collections
|
|
287
|
+
- **[Test Performance](reference/test-performance.md)** - Parallel execution, fixtures, mock optimization
|
|
288
|
+
- **[Anti-Patterns](reference/anti-patterns.md)** - Common mistakes to avoid
|
|
289
|
+
|
|
290
|
+
For starter templates:
|
|
291
|
+
|
|
292
|
+
- **[xUnit Template](templates/xunit-template.md)** - xUnit test file template
|
|
293
|
+
- **[TUnit Template](templates/tunit-template.md)** - TUnit test file template
|