@grimoire-cc/cli 0.13.2 → 0.14.0
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/update.d.ts.map +1 -1
- package/dist/commands/update.js +14 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/enforce.d.ts +3 -1
- package/dist/enforce.d.ts.map +1 -1
- package/dist/enforce.js +18 -6
- package/dist/enforce.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +47 -0
- package/dist/setup.js.map +1 -1
- package/dist/summary.d.ts.map +1 -1
- package/dist/summary.js +9 -0
- package/dist/summary.js.map +1 -1
- package/package.json +1 -1
- package/packs/dev-pack/agents/grimoire.tdd-specialist.md +194 -27
- package/packs/dev-pack/grimoire.json +9 -42
- package/packs/dev-pack/skills/grimoire.conventional-commit/SKILL.md +68 -47
- package/packs/dotnet-pack/agents/grimoire.csharp-coder.md +110 -113
- package/packs/dotnet-pack/grimoire.json +23 -5
- package/packs/dotnet-pack/skills/grimoire.unit-testing-dotnet/SKILL.md +252 -0
- package/packs/{dev-pack/skills/grimoire.tdd-specialist → dotnet-pack/skills/grimoire.unit-testing-dotnet}/reference/anti-patterns.md +78 -0
- package/packs/dotnet-pack/skills/grimoire.unit-testing-dotnet/reference/tdd-workflow-patterns.md +259 -0
- package/packs/go-pack/grimoire.json +19 -0
- package/packs/go-pack/skills/grimoire.unit-testing-go/SKILL.md +256 -0
- package/packs/go-pack/skills/grimoire.unit-testing-go/reference/anti-patterns.md +244 -0
- package/packs/go-pack/skills/grimoire.unit-testing-go/reference/tdd-workflow-patterns.md +259 -0
- package/packs/python-pack/grimoire.json +19 -0
- package/packs/python-pack/skills/grimoire.unit-testing-python/SKILL.md +239 -0
- package/packs/python-pack/skills/grimoire.unit-testing-python/reference/anti-patterns.md +244 -0
- package/packs/python-pack/skills/grimoire.unit-testing-python/reference/tdd-workflow-patterns.md +259 -0
- package/packs/rust-pack/grimoire.json +29 -0
- package/packs/rust-pack/skills/grimoire.unit-testing-rust/SKILL.md +243 -0
- package/packs/rust-pack/skills/grimoire.unit-testing-rust/reference/anti-patterns.md +244 -0
- package/packs/rust-pack/skills/grimoire.unit-testing-rust/reference/tdd-workflow-patterns.md +259 -0
- package/packs/ts-pack/agents/grimoire.typescript-coder.md +36 -1
- package/packs/ts-pack/grimoire.json +27 -1
- package/packs/ts-pack/skills/grimoire.unit-testing-typescript/SKILL.md +255 -0
- package/packs/ts-pack/skills/grimoire.unit-testing-typescript/reference/anti-patterns.md +244 -0
- package/packs/ts-pack/skills/grimoire.unit-testing-typescript/reference/tdd-workflow-patterns.md +259 -0
- package/packs/dev-pack/skills/grimoire.tdd-specialist/SKILL.md +0 -248
- package/packs/dev-pack/skills/grimoire.tdd-specialist/reference/language-frameworks.md +0 -388
- package/packs/dev-pack/skills/grimoire.tdd-specialist/reference/tdd-workflow-patterns.md +0 -135
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/SKILL.md +0 -293
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/anti-patterns.md +0 -329
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/framework-guidelines.md +0 -361
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/parameterized-testing.md +0 -378
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/test-organization.md +0 -476
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/test-performance.md +0 -576
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/templates/tunit-template.md +0 -438
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/templates/xunit-template.md +0 -303
package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/parameterized-testing.md
DELETED
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
# Parameterized Testing
|
|
2
|
-
|
|
3
|
-
Comprehensive guide to parameterized testing in xUnit and TUnit.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [When to Use Each Data Source](#when-to-use-each-data-source)
|
|
8
|
-
- [xUnit Parameterized Testing](#xunit-parameterized-testing)
|
|
9
|
-
- [InlineData](#xunit-inlinedata)
|
|
10
|
-
- [MemberData](#xunit-memberdata)
|
|
11
|
-
- [ClassData](#xunit-classdata)
|
|
12
|
-
- [TUnit Parameterized Testing](#tunit-parameterized-testing)
|
|
13
|
-
- [Arguments](#tunit-arguments)
|
|
14
|
-
- [MethodDataSource](#tunit-methoddatasource)
|
|
15
|
-
- [ClassDataSource](#tunit-classdatasource)
|
|
16
|
-
- [Matrix Testing](#tunit-matrix-testing)
|
|
17
|
-
- [Best Practices](#best-practices)
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## When to Use Each Data Source
|
|
22
|
-
|
|
23
|
-
| Data Source | Use When | xUnit | TUnit |
|
|
24
|
-
| ------------- | ---------- | ------- | ------- |
|
|
25
|
-
| **Inline** | Few simple values (primitives, strings) | `[InlineData]` | `[Arguments]` |
|
|
26
|
-
| **Method** | Computed data, shared within same class | `[MemberData]` | `[MethodDataSource]` |
|
|
27
|
-
| **Class** | Reusable data across multiple test classes | `[ClassData]` | `[ClassDataSource]` |
|
|
28
|
-
| **Matrix** | Test all combinations of inputs | Manual | `[Matrix]` (built-in) |
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## xUnit Parameterized Testing
|
|
33
|
-
|
|
34
|
-
### xUnit InlineData
|
|
35
|
-
|
|
36
|
-
For simple primitive values:
|
|
37
|
-
|
|
38
|
-
```csharp
|
|
39
|
-
[Theory]
|
|
40
|
-
[InlineData("", false)]
|
|
41
|
-
[InlineData("test@example.com", true)]
|
|
42
|
-
[InlineData("invalid", false)]
|
|
43
|
-
[InlineData("user@domain.co.uk", true)]
|
|
44
|
-
public void IsValidEmail_WithVariousInputs_ReturnsExpected(string email, bool expected)
|
|
45
|
-
{
|
|
46
|
-
var result = _sut.IsValidEmail(email);
|
|
47
|
-
Assert.Equal(expected, result);
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### xUnit MemberData
|
|
52
|
-
|
|
53
|
-
For computed or complex data from methods in the same class:
|
|
54
|
-
|
|
55
|
-
```csharp
|
|
56
|
-
[Theory]
|
|
57
|
-
[MemberData(nameof(GetDiscountTestCases))]
|
|
58
|
-
public void CalculateDiscount_WithVariousOrders_ReturnsExpectedDiscount(
|
|
59
|
-
Order order, decimal expectedDiscount, string scenario)
|
|
60
|
-
{
|
|
61
|
-
// Arrange & Act
|
|
62
|
-
var result = _sut.CalculateDiscount(order);
|
|
63
|
-
|
|
64
|
-
// Assert
|
|
65
|
-
Assert.Equal(expectedDiscount, result);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public static IEnumerable<object[]> GetDiscountTestCases()
|
|
69
|
-
{
|
|
70
|
-
yield return new object[]
|
|
71
|
-
{
|
|
72
|
-
new Order { Total = 50m },
|
|
73
|
-
0m,
|
|
74
|
-
"No discount under $100"
|
|
75
|
-
};
|
|
76
|
-
yield return new object[]
|
|
77
|
-
{
|
|
78
|
-
new Order { Total = 100m },
|
|
79
|
-
10m,
|
|
80
|
-
"10% discount at $100"
|
|
81
|
-
};
|
|
82
|
-
yield return new object[]
|
|
83
|
-
{
|
|
84
|
-
new Order { Total = 500m, IsPremiumCustomer = true },
|
|
85
|
-
100m,
|
|
86
|
-
"20% discount for premium over $500"
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
**Referencing data from another class:**
|
|
92
|
-
|
|
93
|
-
```csharp
|
|
94
|
-
[Theory]
|
|
95
|
-
[MemberData(nameof(OrderTestData.ValidOrders), MemberType = typeof(OrderTestData))]
|
|
96
|
-
public async Task ProcessOrder_WithValidOrder_Succeeds(Order order) { }
|
|
97
|
-
|
|
98
|
-
public static class OrderTestData
|
|
99
|
-
{
|
|
100
|
-
public static IEnumerable<object[]> ValidOrders => new[]
|
|
101
|
-
{
|
|
102
|
-
new object[] { CreateStandardOrder() },
|
|
103
|
-
new object[] { CreatePremiumOrder() },
|
|
104
|
-
new object[] { CreateBulkOrder() }
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### xUnit ClassData
|
|
110
|
-
|
|
111
|
-
For reusable test data across multiple test classes:
|
|
112
|
-
|
|
113
|
-
```csharp
|
|
114
|
-
// Define reusable test data class
|
|
115
|
-
public class ValidEmailTestData : IEnumerable<object[]>
|
|
116
|
-
{
|
|
117
|
-
public IEnumerator<object[]> GetEnumerator()
|
|
118
|
-
{
|
|
119
|
-
yield return new object[] { "test@example.com" };
|
|
120
|
-
yield return new object[] { "user.name@domain.co.uk" };
|
|
121
|
-
yield return new object[] { "user+tag@example.org" };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
public class InvalidEmailTestData : IEnumerable<object[]>
|
|
128
|
-
{
|
|
129
|
-
public IEnumerator<object[]> GetEnumerator()
|
|
130
|
-
{
|
|
131
|
-
yield return new object[] { "" };
|
|
132
|
-
yield return new object[] { "notanemail" };
|
|
133
|
-
yield return new object[] { "@nodomain.com" };
|
|
134
|
-
yield return new object[] { "spaces in@email.com" };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Use in multiple test classes
|
|
141
|
-
public class EmailValidatorTests
|
|
142
|
-
{
|
|
143
|
-
[Theory]
|
|
144
|
-
[ClassData(typeof(ValidEmailTestData))]
|
|
145
|
-
public void IsValid_WithValidEmail_ReturnsTrue(string email)
|
|
146
|
-
{
|
|
147
|
-
Assert.True(_sut.IsValid(email));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
[Theory]
|
|
151
|
-
[ClassData(typeof(InvalidEmailTestData))]
|
|
152
|
-
public void IsValid_WithInvalidEmail_ReturnsFalse(string email)
|
|
153
|
-
{
|
|
154
|
-
Assert.False(_sut.IsValid(email));
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
public class EmailServiceTests
|
|
159
|
-
{
|
|
160
|
-
[Theory]
|
|
161
|
-
[ClassData(typeof(ValidEmailTestData))]
|
|
162
|
-
public async Task SendEmail_WithValidAddress_Succeeds(string email)
|
|
163
|
-
{
|
|
164
|
-
// Reusing same test data in different test class
|
|
165
|
-
var result = await _sut.SendAsync(email, "Subject", "Body");
|
|
166
|
-
Assert.True(result.Success);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
|
-
## TUnit Parameterized Testing
|
|
174
|
-
|
|
175
|
-
### TUnit Arguments
|
|
176
|
-
|
|
177
|
-
For simple values (equivalent to xUnit's InlineData):
|
|
178
|
-
|
|
179
|
-
```csharp
|
|
180
|
-
[Test]
|
|
181
|
-
[Arguments("", false)]
|
|
182
|
-
[Arguments("test@example.com", true)]
|
|
183
|
-
[Arguments("invalid", false)]
|
|
184
|
-
[Arguments("user@domain.co.uk", true)]
|
|
185
|
-
public async Task IsValidEmail_WithVariousInputs_ReturnsExpected(string email, bool expected)
|
|
186
|
-
{
|
|
187
|
-
var result = _sut.IsValidEmail(email);
|
|
188
|
-
await Assert.That(result).IsEqualTo(expected);
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### TUnit MethodDataSource
|
|
193
|
-
|
|
194
|
-
For computed data (equivalent to xUnit's MemberData):
|
|
195
|
-
|
|
196
|
-
```csharp
|
|
197
|
-
[Test]
|
|
198
|
-
[MethodDataSource(nameof(GetDiscountTestCases))]
|
|
199
|
-
public async Task CalculateDiscount_WithVariousOrders_ReturnsExpectedDiscount(
|
|
200
|
-
Order order, decimal expectedDiscount, string scenario)
|
|
201
|
-
{
|
|
202
|
-
var result = _sut.CalculateDiscount(order);
|
|
203
|
-
await Assert.That(result).IsEqualTo(expectedDiscount);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// TUnit supports tuples for cleaner syntax
|
|
207
|
-
public static IEnumerable<(Order Order, decimal ExpectedDiscount, string Scenario)> GetDiscountTestCases()
|
|
208
|
-
{
|
|
209
|
-
yield return (
|
|
210
|
-
new Order { Total = 50m },
|
|
211
|
-
0m,
|
|
212
|
-
"No discount under $100"
|
|
213
|
-
);
|
|
214
|
-
yield return (
|
|
215
|
-
new Order { Total = 100m },
|
|
216
|
-
10m,
|
|
217
|
-
"10% discount at $100"
|
|
218
|
-
);
|
|
219
|
-
yield return (
|
|
220
|
-
new Order { Total = 500m, IsPremiumCustomer = true },
|
|
221
|
-
100m,
|
|
222
|
-
"20% discount for premium over $500"
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### TUnit ClassDataSource
|
|
228
|
-
|
|
229
|
-
For reusable data across test classes:
|
|
230
|
-
|
|
231
|
-
```csharp
|
|
232
|
-
// Define reusable test data source
|
|
233
|
-
public class ValidEmailDataSource : DataSourceGeneratorAttribute<string>
|
|
234
|
-
{
|
|
235
|
-
public override IEnumerable<string> GenerateDataSources(DataGeneratorMetadata metadata)
|
|
236
|
-
{
|
|
237
|
-
yield return "test@example.com";
|
|
238
|
-
yield return "user.name@domain.co.uk";
|
|
239
|
-
yield return "user+tag@example.org";
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
public class InvalidEmailDataSource : DataSourceGeneratorAttribute<string>
|
|
244
|
-
{
|
|
245
|
-
public override IEnumerable<string> GenerateDataSources(DataGeneratorMetadata metadata)
|
|
246
|
-
{
|
|
247
|
-
yield return "";
|
|
248
|
-
yield return "notanemail";
|
|
249
|
-
yield return "@nodomain.com";
|
|
250
|
-
yield return "spaces in@email.com";
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Use in tests
|
|
255
|
-
public class EmailValidatorTests
|
|
256
|
-
{
|
|
257
|
-
[Test]
|
|
258
|
-
[ValidEmailDataSource]
|
|
259
|
-
public async Task IsValid_WithValidEmail_ReturnsTrue(string email)
|
|
260
|
-
{
|
|
261
|
-
await Assert.That(_sut.IsValid(email)).IsTrue();
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
[Test]
|
|
265
|
-
[InvalidEmailDataSource]
|
|
266
|
-
public async Task IsValid_WithInvalidEmail_ReturnsFalse(string email)
|
|
267
|
-
{
|
|
268
|
-
await Assert.That(_sut.IsValid(email)).IsFalse();
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### TUnit Matrix Testing
|
|
274
|
-
|
|
275
|
-
TUnit's unique feature for combinatorial testing:
|
|
276
|
-
|
|
277
|
-
```csharp
|
|
278
|
-
// Test all combinations automatically
|
|
279
|
-
// This generates 3 × 2 × 2 = 12 test cases
|
|
280
|
-
[Test]
|
|
281
|
-
[Matrix("Small", "Medium", "Large")]
|
|
282
|
-
[Matrix("Standard", "Express")]
|
|
283
|
-
[Matrix(true, false)]
|
|
284
|
-
public async Task CalculateShipping_MatrixTest_ReturnsValidPrice(
|
|
285
|
-
string size, string shippingMethod, bool isInternational)
|
|
286
|
-
{
|
|
287
|
-
var result = await _sut.CalculateShippingAsync(size, shippingMethod, isInternational);
|
|
288
|
-
|
|
289
|
-
await Assert.That(result).IsGreaterThan(0);
|
|
290
|
-
}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## Best Practices
|
|
296
|
-
|
|
297
|
-
### 1. Include Scenario Descriptions
|
|
298
|
-
|
|
299
|
-
```csharp
|
|
300
|
-
// xUnit - Add description as last parameter
|
|
301
|
-
[Theory]
|
|
302
|
-
[InlineData(100, 10, "Standard 10% discount")]
|
|
303
|
-
[InlineData(500, 75, "Premium 15% discount")]
|
|
304
|
-
[InlineData(50, 0, "No discount under threshold")]
|
|
305
|
-
public void CalculateDiscount_Scenarios(decimal total, decimal expected, string scenario)
|
|
306
|
-
{
|
|
307
|
-
// scenario parameter helps identify which case failed
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// TUnit - Use DisplayName
|
|
311
|
-
[Test]
|
|
312
|
-
[Arguments("", false, DisplayName = "Empty string should be invalid")]
|
|
313
|
-
[Arguments("test@example.com", true, DisplayName = "Standard email should be valid")]
|
|
314
|
-
public async Task IsValidEmail_WithDisplayNames(string email, bool expected)
|
|
315
|
-
{
|
|
316
|
-
await Assert.That(_sut.IsValid(email)).IsEqualTo(expected);
|
|
317
|
-
}
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
### 2. Group Related Test Data
|
|
321
|
-
|
|
322
|
-
```csharp
|
|
323
|
-
public static class OrderTestData
|
|
324
|
-
{
|
|
325
|
-
public static IEnumerable<object[]> ValidOrders => new[]
|
|
326
|
-
{
|
|
327
|
-
new object[] { CreateStandardOrder() },
|
|
328
|
-
new object[] { CreatePremiumOrder() },
|
|
329
|
-
new object[] { CreateBulkOrder() }
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
public static IEnumerable<object[]> InvalidOrders => new[]
|
|
333
|
-
{
|
|
334
|
-
new object[] { CreateEmptyOrder() },
|
|
335
|
-
new object[] { CreateNegativeQuantityOrder() },
|
|
336
|
-
new object[] { CreateExpiredOrder() }
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
### 3. Test Boundary Conditions
|
|
342
|
-
|
|
343
|
-
```csharp
|
|
344
|
-
[Theory]
|
|
345
|
-
[InlineData(int.MinValue)] // Minimum boundary
|
|
346
|
-
[InlineData(-1)] // Just below zero
|
|
347
|
-
[InlineData(0)] // Zero boundary
|
|
348
|
-
[InlineData(1)] // Just above zero
|
|
349
|
-
[InlineData(int.MaxValue)] // Maximum boundary
|
|
350
|
-
public void ProcessValue_WithBoundaryValues_HandlesCorrectly(int value)
|
|
351
|
-
{
|
|
352
|
-
// Test behavior at boundaries
|
|
353
|
-
}
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
### 4. Avoid Too Many Parameters
|
|
357
|
-
|
|
358
|
-
```csharp
|
|
359
|
-
// BAD: Too many parameters, hard to read
|
|
360
|
-
[Theory]
|
|
361
|
-
[InlineData("John", "Doe", 25, "john@test.com", "123 Main St", "NYC", "NY", "10001", true)]
|
|
362
|
-
public void CreateUser_TooManyParams(string first, string last, int age, ...) { }
|
|
363
|
-
|
|
364
|
-
// GOOD: Use a test data object
|
|
365
|
-
[Theory]
|
|
366
|
-
[MemberData(nameof(GetUserTestCases))]
|
|
367
|
-
public void CreateUser_WithTestObject(UserTestCase testCase)
|
|
368
|
-
{
|
|
369
|
-
var result = _sut.CreateUser(testCase.Input);
|
|
370
|
-
Assert.Equal(testCase.ExpectedResult, result.Success);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
public record UserTestCase(UserInput Input, bool ExpectedResult, string Scenario);
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
### 5. Keep Test Data Close to Tests
|
|
377
|
-
|
|
378
|
-
For data used by only one test class, keep it in the same file. Only extract to separate classes when shared across multiple test files.
|