@@ -178,27 +178,13 @@ public class TestApplication : IAsyncInitializer, IAsyncDisposable
178178}
179179```
180180
181- ### 3. Create a Data Source Attribute
181+ ### 3. Write Integration Tests
182182
183183``` csharp
184- // This attribute will provide a fully initialized TestApplication to tests
185- public class TestApplicationAttribute : DataSourceGeneratorAttribute <TestApplication >
186- {
187- public override IEnumerable <TestApplication > GenerateDataSources (DataGeneratorMetadata metadata )
188- {
189- // Return a single instance that will be initialized by TUnit
190- yield return new TestApplication ();
191- }
192- }
193-
194- ### 4. Write Integration Tests
195-
196- ```csharp
197- [TestClass ]
198184public class UserApiIntegrationTests
199185{
200186 [Test ]
201- [TestApplication ]
187+ [ClassDataSource < TestApplication >( Shared = SharedType . PerClass ) ]
202188 public async Task CreateUser_Should_Store_In_Database (TestApplication app )
203189 {
204190 // Arrange
@@ -224,7 +210,7 @@ public class UserApiIntegrationTests
224210 }
225211
226212 [Test ]
227- [TestApplication ]
213+ [ClassDataSource < TestApplication >( Shared = SharedType . PerClass ) ]
228214 public async Task CreateUser_Should_Cache_In_Redis (TestApplication app )
229215 {
230216 // Arrange
@@ -247,135 +233,101 @@ public class UserApiIntegrationTests
247233 }
248234}
249235
250- ### 5. Advanced Scenario: Custom Test Data with Initialized Context
236+ ### 4. Advanced Scenario: Parameterized Tests with Seeded Data
237+
238+ For parameterized tests that need pre - seeded data , combine `ClassDataSource ` with `MethodDataSource `:
251239
252240```csharp
253- // Data source that provides test scenarios with pre-populated data
254- public class OrderTestScenarioAttribute : AsyncDataSourceGeneratorAttribute <OrderTestScenario >
241+ public class OrderProcessingTests
255242{
256- [ClassDataSource <TestApplication >]
257- public required TestApplication App { get ; init ; }
258-
259- public override async IAsyncEnumerable <OrderTestScenario > GenerateDataSourcesAsync (DataGeneratorMetadata metadata )
260- {
261- // App is already initialized here with Redis and SQL Server running
262-
263- // Seed test data
264- var customerId = await CreateTestCustomer ();
265- var productIds = await CreateTestProducts ();
266-
267- yield return new OrderTestScenario
268- {
269- Name = " Valid order with single item" ,
270- App = App ,
271- CustomerId = customerId ,
272- OrderItems = new []
273- {
274- new OrderItem { ProductId = productIds [0 ], Quantity = 1 }
275- },
276- ExpectedTotal = 29 . 99 m
277- };
278-
279- yield return new OrderTestScenario
280- {
281- Name = " Valid order with multiple items" ,
282- App = App ,
283- CustomerId = customerId ,
284- OrderItems = new []
285- {
286- new OrderItem { ProductId = productIds [0 ], Quantity = 2 },
287- new OrderItem { ProductId = productIds [1 ], Quantity = 1 }
288- },
289- ExpectedTotal = 89 . 97 m
290- };
291-
292- yield return new OrderTestScenario
293- {
294- Name = " Order exceeding stock" ,
295- App = App ,
296- CustomerId = customerId ,
297- OrderItems = new []
298- {
299- new OrderItem { ProductId = productIds [0 ], Quantity = 1000 }
300- },
301- ExpectedException = typeof (InsufficientStockException )
302- };
303- }
304-
305- private async Task <Guid > CreateTestCustomer ()
243+ // Helper method to seed test data
244+ private static async Task <(Guid CustomerId , Guid [] ProductIds )> SeedTestData (TestApplication app )
306245 {
307- using var scope = App .Services .CreateScope ();
246+ using var scope = app .Services .CreateScope ();
308247 var dbContext = scope .ServiceProvider .GetRequiredService <ApplicationDbContext >();
309-
248+
249+ // Create test customer
310250 var customer = new Customer
311251 {
312252 Id = Guid .NewGuid (),
313253 Name = " Test Customer" ,
314254 Email = " customer@test.com"
315255 };
316-
317256 dbContext .Customers .Add (customer );
318- await dbContext .SaveChangesAsync ();
319-
320- return customer .Id ;
321- }
322-
323- private async Task <Guid []> CreateTestProducts ()
324- {
325- using var scope = App .Services .CreateScope ();
326- var dbContext = scope .ServiceProvider .GetRequiredService <ApplicationDbContext >();
327-
257+
258+ // Create test products
328259 var products = new []
329260 {
330261 new Product { Id = Guid .NewGuid (), Name = " Widget" , Price = 29 . 99 m , Stock = 100 },
331262 new Product { Id = Guid .NewGuid (), Name = " Gadget" , Price = 59 . 99 m , Stock = 50 }
332263 };
333-
334264 dbContext .Products .AddRange (products );
265+
335266 await dbContext .SaveChangesAsync ();
336-
337- return products .Select (p => p .Id ).ToArray ();
267+
268+ return ( customer . Id , products .Select (p => p .Id ).ToArray () );
338269 }
339- }
340270
341- public class OrderTestScenario
342- {
343- public required string Name { get ; init ; }
344- public required TestApplication App { get ; init ; }
345- public required Guid CustomerId { get ; init ; }
346- public required OrderItem [] OrderItems { get ; init ; }
347- public decimal ? ExpectedTotal { get ; init ; }
348- public Type ? ExpectedException { get ; init ; }
349- }
271+ // Provide test scenarios
272+ private static IEnumerable <(string Name , Func <Guid , Guid [], OrderItem []> Items , decimal ? ExpectedTotal , bool ShouldFail )> OrderScenarios ()
273+ {
274+ yield return (
275+ " Valid order with single item" ,
276+ (customerId , productIds ) => new [] { new OrderItem { ProductId = productIds [0 ], Quantity = 1 } },
277+ 29 . 99 m ,
278+ false
279+ );
280+
281+ yield return (
282+ " Valid order with multiple items" ,
283+ (customerId , productIds ) => new []
284+ {
285+ new OrderItem { ProductId = productIds [0 ], Quantity = 2 },
286+ new OrderItem { ProductId = productIds [1 ], Quantity = 1 }
287+ },
288+ 89 . 97 m ,
289+ false
290+ );
291+
292+ yield return (
293+ " Order exceeding stock" ,
294+ (customerId , productIds ) => new [] { new OrderItem { ProductId = productIds [0 ], Quantity = 1000 } },
295+ null ,
296+ true
297+ );
298+ }
350299
351- // Use the test scenarios
352- [TestClass ]
353- public class OrderProcessingTests
354- {
355300 [Test ]
356- [OrderTestScenario ]
357- public async Task ProcessOrder_Scenarios (OrderTestScenario scenario )
301+ [ClassDataSource <TestApplication >(Shared = SharedType .PerClass )]
302+ [MethodDataSource (nameof (OrderScenarios ))]
303+ public async Task ProcessOrder_Scenarios (
304+ TestApplication app ,
305+ string scenarioName ,
306+ Func <Guid , Guid [], OrderItem []> itemsFactory ,
307+ decimal ? expectedTotal ,
308+ bool shouldFail )
358309 {
359- // Arrange
310+ // Arrange - Seed data first
311+ var (customerId , productIds ) = await SeedTestData (app );
360312 var orderRequest = new CreateOrderRequest
361313 {
362- CustomerId = scenario . CustomerId ,
363- Items = scenario . OrderItems
314+ CustomerId = customerId ,
315+ Items = itemsFactory ( customerId , productIds )
364316 };
365-
317+
366318 // Act
367- var response = await scenario . App .Client .PostAsJsonAsync (" /api/orders" , orderRequest );
368-
319+ var response = await app .Client .PostAsJsonAsync (" /api/orders" , orderRequest );
320+
369321 // Assert
370- if (scenario . ExpectedException != null )
322+ if (shouldFail )
371323 {
372- response .StatusCode . Should (). Be (HttpStatusCode .BadRequest );
324+ await Assert . That ( response .StatusCode ). IsEqualTo (HttpStatusCode .BadRequest );
373325 }
374326 else
375327 {
376- response .StatusCode . Should (). Be (HttpStatusCode .Created );
328+ await Assert . That ( response .StatusCode ). IsEqualTo (HttpStatusCode .Created );
377329 var order = await response .Content .ReadFromJsonAsync <OrderResponse >();
378- order ! .Total . Should (). Be ( scenario . ExpectedTotal );
330+ await Assert . That ( order ! .Total ). IsEqualTo ( expectedTotal );
379331 }
380332 }
381333}
0 commit comments