@grimoire-cc/cli 0.13.1 → 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.
@@ -0,0 +1,361 @@
1
+ # Framework Guidelines
2
+
3
+ Detailed guidelines for xUnit and TUnit testing frameworks.
4
+
5
+ ## Table of Contents
6
+
7
+ - [xUnit Guidelines](#xunit-guidelines)
8
+ - [Attributes](#xunit-attributes)
9
+ - [Lifecycle](#xunit-lifecycle)
10
+ - [Assertions](#xunit-assertions)
11
+ - [TUnit Guidelines](#tunit-guidelines)
12
+ - [Attributes](#tunit-attributes)
13
+ - [Lifecycle](#tunit-lifecycle)
14
+ - [Assertions](#tunit-assertions)
15
+ - [Assertion Chaining](#tunit-assertion-chaining)
16
+
17
+ ---
18
+
19
+ ## xUnit Guidelines
20
+
21
+ xUnit is the default framework for .NET testing. It uses constructor injection for test setup and implements `IDisposable` for cleanup.
22
+
23
+ ### xUnit Attributes
24
+
25
+ | Attribute | Purpose |
26
+ | ----------- | --------- |
27
+ | `[Fact]` | Single test case |
28
+ | `[Theory]` | Parameterized test |
29
+ | `[InlineData]` | Simple inline test data |
30
+ | `[MemberData]` | Method-based test data |
31
+ | `[ClassData]` | Reusable test data class |
32
+ | `[Trait]` | Test categorization |
33
+ | `[Collection]` | Shared fixture across classes |
34
+
35
+ **Examples:**
36
+
37
+ ```csharp
38
+ // Simple test
39
+ [Fact]
40
+ public async Task GetOrder_WithValidId_ReturnsOrder()
41
+ {
42
+ var result = await _sut.GetOrderAsync(validId);
43
+ Assert.NotNull(result);
44
+ }
45
+
46
+ // Parameterized test with inline data
47
+ [Theory]
48
+ [InlineData("", false)]
49
+ [InlineData("invalid", false)]
50
+ [InlineData("test@example.com", true)]
51
+ public void IsValidEmail_WithVariousInputs_ReturnsExpectedResult(string email, bool expected)
52
+ {
53
+ var result = _sut.IsValidEmail(email);
54
+ Assert.Equal(expected, result);
55
+ }
56
+
57
+ // Parameterized test with method data
58
+ [Theory]
59
+ [MemberData(nameof(GetOrderTestCases))]
60
+ public async Task ProcessOrder_WithVariousOrders_BehavesCorrectly(
61
+ Order order, bool expectedSuccess, string expectedMessage)
62
+ {
63
+ var result = await _sut.ProcessOrderAsync(order);
64
+ Assert.Equal(expectedSuccess, result.Success);
65
+ }
66
+
67
+ public static IEnumerable<object[]> GetOrderTestCases()
68
+ {
69
+ yield return new object[] { CreateValidOrder(), true, "Success" };
70
+ yield return new object[] { CreateInvalidOrder(), false, "Invalid order" };
71
+ }
72
+ ```
73
+
74
+ ### xUnit Lifecycle
75
+
76
+ ```csharp
77
+ public class OrderServiceTests : IDisposable
78
+ {
79
+ // Fresh instances for each test via constructor
80
+ private readonly Mock<IOrderRepository> _mockRepository;
81
+ private readonly FakeLogger<OrderService> _fakeLogger;
82
+ private readonly OrderService _sut;
83
+
84
+ public OrderServiceTests()
85
+ {
86
+ _mockRepository = new Mock<IOrderRepository>();
87
+ _fakeLogger = new FakeLogger<OrderService>();
88
+ _sut = new OrderService(_fakeLogger, _mockRepository.Object);
89
+ }
90
+
91
+ public void Dispose()
92
+ {
93
+ // Cleanup if needed
94
+ }
95
+ }
96
+ ```
97
+
98
+ **Async Lifecycle:**
99
+
100
+ ```csharp
101
+ public class DatabaseTests : IAsyncLifetime
102
+ {
103
+ private TestDatabase _database;
104
+
105
+ public async Task InitializeAsync()
106
+ {
107
+ _database = await TestDatabase.CreateAsync();
108
+ }
109
+
110
+ public async Task DisposeAsync()
111
+ {
112
+ await _database.CleanupAsync();
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### xUnit Assertions
118
+
119
+ ```csharp
120
+ // Basic assertions
121
+ Assert.True(condition);
122
+ Assert.False(condition);
123
+ Assert.Null(value);
124
+ Assert.NotNull(value);
125
+ Assert.Equal(expected, actual);
126
+ Assert.NotEqual(expected, actual);
127
+
128
+ // Collection assertions
129
+ Assert.Empty(collection);
130
+ Assert.NotEmpty(collection);
131
+ Assert.Contains(item, collection);
132
+ Assert.DoesNotContain(item, collection);
133
+ Assert.All(collection, item => Assert.True(item.IsValid));
134
+ Assert.Single(collection);
135
+
136
+ // String assertions
137
+ Assert.StartsWith("prefix", value);
138
+ Assert.EndsWith("suffix", value);
139
+ Assert.Contains("substring", value);
140
+
141
+ // Exception assertions
142
+ await Assert.ThrowsAsync<ArgumentNullException>(() => _sut.MethodAsync(null));
143
+ var ex = await Assert.ThrowsAsync<CustomException>(() => _sut.MethodAsync(input));
144
+ Assert.Equal("expected message", ex.Message);
145
+
146
+ // Type assertions
147
+ Assert.IsType<ExpectedType>(value);
148
+ Assert.IsAssignableFrom<IInterface>(value);
149
+ ```
150
+
151
+ ---
152
+
153
+ ## TUnit Guidelines
154
+
155
+ TUnit is recommended for new .NET 8+ projects. It's async-first with built-in fluent assertions.
156
+
157
+ ### TUnit Attributes
158
+
159
+ | Attribute | Purpose | xUnit Equivalent |
160
+ | ----------- | --------- | ------------------ |
161
+ | `[Test]` | All test methods | `[Fact]` |
162
+ | `[Arguments]` | Inline test data | `[InlineData]` |
163
+ | `[MethodDataSource]` | Method-based data | `[MemberData]` |
164
+ | `[ClassDataSource]` | Reusable data class | `[ClassData]` |
165
+ | `[Matrix]` | Combinatorial testing | N/A (unique to TUnit) |
166
+ | `[Category]` | Test categorization | `[Trait]` |
167
+ | `[Timeout]` | Test timeout | N/A |
168
+ | `[Retry]` | Retry flaky tests | N/A |
169
+ | `[NotInParallel]` | Sequential execution | `[Collection]` |
170
+
171
+ **Examples:**
172
+
173
+ ```csharp
174
+ // Simple test
175
+ [Test]
176
+ public async Task GetOrder_WithValidId_ReturnsOrder()
177
+ {
178
+ var result = await _sut.GetOrderAsync(validId);
179
+ await Assert.That(result).IsNotNull();
180
+ }
181
+
182
+ // Parameterized test with arguments
183
+ [Test]
184
+ [Arguments("", false)]
185
+ [Arguments("invalid", false)]
186
+ [Arguments("test@example.com", true)]
187
+ public async Task IsValidEmail_WithVariousInputs_ReturnsExpectedResult(string email, bool expected)
188
+ {
189
+ var result = _sut.IsValidEmail(email);
190
+ await Assert.That(result).IsEqualTo(expected);
191
+ }
192
+
193
+ // Parameterized test with method data source
194
+ [Test]
195
+ [MethodDataSource(nameof(GetOrderTestCases))]
196
+ public async Task ProcessOrder_WithVariousOrders_BehavesCorrectly(Order order, bool expectedSuccess)
197
+ {
198
+ var result = await _sut.ProcessOrderAsync(order);
199
+ await Assert.That(result.IsSuccess).IsEqualTo(expectedSuccess);
200
+ }
201
+
202
+ // TUnit supports tuples for cleaner syntax
203
+ public static IEnumerable<(Order, bool)> GetOrderTestCases()
204
+ {
205
+ yield return (CreateValidOrder(), true);
206
+ yield return (CreateInvalidOrder(), false);
207
+ }
208
+ ```
209
+
210
+ ### TUnit Lifecycle
211
+
212
+ ```csharp
213
+ public class OrderServiceTests : IAsyncDisposable
214
+ {
215
+ private readonly Mock<IOrderRepository> _mockRepository;
216
+ private readonly FakeLogger<OrderService> _fakeLogger;
217
+ private readonly OrderService _sut;
218
+
219
+ public OrderServiceTests()
220
+ {
221
+ _mockRepository = new Mock<IOrderRepository>();
222
+ _fakeLogger = new FakeLogger<OrderService>();
223
+ _sut = new OrderService(_fakeLogger, _mockRepository.Object);
224
+ }
225
+
226
+ public ValueTask DisposeAsync()
227
+ {
228
+ // Cleanup if needed
229
+ return ValueTask.CompletedTask;
230
+ }
231
+ }
232
+ ```
233
+
234
+ **Attribute-based Lifecycle:**
235
+
236
+ ```csharp
237
+ public class DatabaseTests
238
+ {
239
+ [Before(Test)]
240
+ public async Task SetupBeforeEachTest() { }
241
+
242
+ [After(Test)]
243
+ public async Task CleanupAfterEachTest() { }
244
+
245
+ [Before(Class)]
246
+ public static async Task SetupBeforeAllTests() { }
247
+
248
+ [After(Class)]
249
+ public static async Task CleanupAfterAllTests() { }
250
+ }
251
+ ```
252
+
253
+ ### TUnit Assertions
254
+
255
+ All TUnit assertions are awaitable, providing better stack traces and true async behavior.
256
+
257
+ ```csharp
258
+ // Basic assertions
259
+ await Assert.That(condition).IsTrue();
260
+ await Assert.That(condition).IsFalse();
261
+ await Assert.That(value).IsNull();
262
+ await Assert.That(value).IsNotNull();
263
+ await Assert.That(actual).IsEqualTo(expected);
264
+ await Assert.That(actual).IsNotEqualTo(expected);
265
+
266
+ // Collection assertions
267
+ await Assert.That(collection).IsEmpty();
268
+ await Assert.That(collection).IsNotEmpty();
269
+ await Assert.That(collection).Contains("item");
270
+ await Assert.That(collection).DoesNotContain("item");
271
+ await Assert.That(collection).HasCount(3);
272
+ await Assert.That(collection).IsEquivalentTo(expectedItems);
273
+
274
+ // String assertions
275
+ await Assert.That(message).StartsWith("Error:");
276
+ await Assert.That(message).EndsWith("failed");
277
+ await Assert.That(message).Contains("substring");
278
+
279
+ // Numeric assertions with tolerance
280
+ await Assert.That(result).IsEqualTo(expected).Within(0.001);
281
+ await Assert.That(timestamp).IsEqualTo(DateTime.Now).Within(TimeSpan.FromSeconds(1));
282
+
283
+ // Exception assertions
284
+ await Assert.That(() => _sut.GetOrderAsync(invalidId))
285
+ .ThrowsException()
286
+ .OfType<OrderNotFoundException>()
287
+ .WithProperty(e => e.OrderId, invalidId);
288
+
289
+ // Type assertions
290
+ await Assert.That(value).IsTypeOf<ExpectedType>();
291
+ await Assert.That(value).IsAssignableTo<IInterface>();
292
+ ```
293
+
294
+ ### TUnit Assertion Chaining
295
+
296
+ TUnit allows fluent chaining with `.And`:
297
+
298
+ ```csharp
299
+ // Chain multiple assertions
300
+ await Assert.That(result)
301
+ .IsNotNull()
302
+ .And.HasProperty(r => r.Items)
303
+ .And.HasCount().GreaterThan(0);
304
+
305
+ // Numeric range
306
+ await Assert.That(result).IsGreaterThan(0);
307
+ await Assert.That(result).IsLessThan(1000);
308
+ await Assert.That(result).IsBetween(1, 100);
309
+ ```
310
+
311
+ ### TUnit-Specific Features
312
+
313
+ **Timeout:**
314
+
315
+ ```csharp
316
+ [Test]
317
+ [Timeout(5000)] // 5 seconds
318
+ public async Task LongRunningOperation_CompletesWithinTimeout()
319
+ {
320
+ var result = await _sut.ProcessAsync();
321
+ await Assert.That(result.IsSuccess).IsTrue();
322
+ }
323
+ ```
324
+
325
+ **Retry:**
326
+
327
+ ```csharp
328
+ [Test]
329
+ [Retry(3)]
330
+ public async Task OccasionallySlowTest()
331
+ {
332
+ // Will retry up to 3 times if it fails
333
+ }
334
+ ```
335
+
336
+ **Matrix Testing (Combinatorial):**
337
+
338
+ ```csharp
339
+ // Tests all combinations: 3 sizes × 2 methods × 2 booleans = 12 tests
340
+ [Test]
341
+ [Matrix("Small", "Medium", "Large")]
342
+ [Matrix("Standard", "Express")]
343
+ [Matrix(true, false)]
344
+ public async Task CalculateShipping_MatrixTest(string size, string method, bool isInternational)
345
+ {
346
+ var result = await _sut.CalculateShippingAsync(size, method, isInternational);
347
+ await Assert.That(result).IsGreaterThan(0);
348
+ }
349
+ ```
350
+
351
+ **Custom Display Names:**
352
+
353
+ ```csharp
354
+ [Test]
355
+ [Arguments("", false, DisplayName = "Empty string should be invalid")]
356
+ [Arguments("test@example.com", true, DisplayName = "Standard email should be valid")]
357
+ public async Task IsValidEmail_WithDisplayNames(string email, bool expected)
358
+ {
359
+ await Assert.That(_sut.IsValid(email)).IsEqualTo(expected);
360
+ }
361
+ ```