Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
509 changes: 509 additions & 0 deletions docs/docs/advanced/exception-handling.md

Large diffs are not rendered by default.

446 changes: 446 additions & 0 deletions docs/docs/advanced/extension-points.md

Large diffs are not rendered by default.

556 changes: 556 additions & 0 deletions docs/docs/advanced/performance-best-practices.md

Large diffs are not rendered by default.

288 changes: 288 additions & 0 deletions docs/docs/assertions/awaiting.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,291 @@ This won't:

TUnit is able to take in asynchronous delegates. To be able to assert on these, we need to execute the code. We want to avoid sync-over-async, as this can cause problems and block the thread pool, slowing down your test suite.
And with how fast .NET has become, the overhead of `Task`s and `async` methods shouldn't be noticable.

## Complex Assertion Examples

### Chaining Multiple Assertions

You can chain multiple assertions together for more complex validations:

```csharp
[Test]
public async Task ComplexObjectValidation()
{
var user = await GetUserAsync("john.doe");

// Chain multiple property assertions
await Assert.That(user)
.IsNotNull()
.And.HasProperty(u => u.Email).EqualTo("[email protected]")
.And.HasProperty(u => u.Age).GreaterThan(18)
.And.HasProperty(u => u.Roles).Contains("Admin");
}
```

### Collection Assertions with Complex Conditions

```csharp
[Test]
public async Task ComplexCollectionAssertions()
{
var orders = await GetOrdersAsync();

// Assert multiple conditions on a collection
await Assert.That(orders)
.HasCount().GreaterThan(0)
.And.Contains(o => o.Status == OrderStatus.Completed)
.And.DoesNotContain(o => o.Total < 0)
.And.HasDistinctItems(new OrderIdComparer());

// Assert on filtered subset
var completedOrders = orders.Where(o => o.Status == OrderStatus.Completed);
await Assert.That(completedOrders)
.All(o => o.CompletedDate != null)
.And.Any(o => o.Total > 1000);
}
```

### Async Operation Assertions

```csharp
[Test]
public async Task AsyncOperationAssertions()
{
// Assert that async operation completes within time limit
await Assert.That(async () => await LongRunningOperationAsync())
.CompletesWithin(TimeSpan.FromSeconds(5));

// Assert that async operation throws specific exception
await Assert.That(async () => await RiskyOperationAsync())
.Throws<InvalidOperationException>()
.WithMessage().Containing("connection failed");

// Assert on result of async operation
await Assert.That(() => CalculateAsync(10, 20))
.ResultsIn(30)
.Within(TimeSpan.FromSeconds(1));
}
```

### Nested Object Assertions

```csharp
[Test]
public async Task NestedObjectAssertions()
{
var company = await GetCompanyAsync();

await Assert.That(company)
.IsNotNull()
.And.HasProperty(c => c.Name).EqualTo("TechCorp")
.And.HasProperty(c => c.Address).Satisfies(address =>
Assert.That(address)
.HasProperty(a => a.City).EqualTo("Seattle")
.And.HasProperty(a => a.ZipCode).Matches(@"^\d{5}$")
)
.And.HasProperty(c => c.Employees).Satisfies(employees =>
Assert.That(employees)
.HasCount().Between(100, 500)
.And.All(e => e.Email.EndsWith("@techcorp.com"))
);
}
```

### Exception Assertions with Details

```csharp
[Test]
public async Task DetailedExceptionAssertions()
{
var invalidData = new { Id = -1, Name = "" };

// Assert exception with specific properties
await Assert.That(() => ProcessDataAsync(invalidData))
.Throws<ValidationException>()
.WithMessage().EqualTo("Validation failed")
.And.WithInnerException<ArgumentException>()
.WithParameterName("data");

// Assert aggregate exception
await Assert.That(() => ParallelOperationAsync())
.Throws<AggregateException>()
.Where(ex => ex.InnerExceptions.Count == 3)
.And.Where(ex => ex.InnerExceptions.All(e => e is TaskCanceledException));
}
```

### Custom Assertion Conditions

```csharp
[Test]
public async Task CustomAssertionConditions()
{
var measurements = await GetMeasurementsAsync();

// Use custom conditions for complex validations
await Assert.That(measurements)
.Satisfies(m => {
var average = m.Average();
var stdDev = CalculateStandardDeviation(m);
return stdDev < average * 0.1; // Less than 10% deviation
}, "Measurements should have low standard deviation");

// Combine built-in and custom assertions
await Assert.That(measurements)
.HasCount().GreaterThan(100)
.And.All(m => m > 0)
.And.Satisfies(IsNormallyDistributed, "Data should be normally distributed");
}
```

### DateTime and TimeSpan Assertions

```csharp
[Test]
public async Task DateTimeAssertions()
{
var order = await CreateOrderAsync();

// Complex datetime assertions
await Assert.That(order.CreatedAt)
.IsAfter(DateTime.UtcNow.AddMinutes(-1))
.And.IsBefore(DateTime.UtcNow.AddMinutes(1))
.And.HasKind(DateTimeKind.Utc);

// TimeSpan assertions
var processingTime = order.CompletedAt - order.CreatedAt;
await Assert.That(processingTime)
.IsLessThan(TimeSpan.FromMinutes(5))
.And.IsGreaterThan(TimeSpan.Zero);
}
```

### Floating Point Comparisons

```csharp
[Test]
public async Task FloatingPointAssertions()
{
var calculations = await PerformComplexCalculationsAsync();

// Use tolerance for floating point comparisons
await Assert.That(calculations.Pi)
.IsEqualTo(Math.PI).Within(0.0001);

// Assert on collections of floating point numbers
await Assert.That(calculations.Results)
.All(r => Math.Abs(r) < 1000000) // No overflow
.And.Contains(42.0).Within(0.1) // Contains approximately 42
.And.HasSum().EqualTo(expectedSum).Within(0.01);
}
```

### String Pattern Matching

```csharp
[Test]
public async Task StringPatternAssertions()
{
var logs = await GetLogEntriesAsync();

// Complex string assertions
await Assert.That(logs)
.All(log => log.Matches(@"^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]"))
.And.Any(log => log.Contains("ERROR"))
.And.None(log => log.Contains("SENSITIVE_DATA"));

// Assert on formatted output
var report = await GenerateReportAsync();
await Assert.That(report)
.StartsWith("Report Generated:")
.And.Contains("Total Items:")
.And.DoesNotContain("null")
.And.HasLength().Between(1000, 5000);
}
```

### Combining Or and And Conditions

```csharp
[Test]
public async Task ComplexLogicalConditions()
{
var product = await GetProductAsync();

// Complex logical combinations
await Assert.That(product)
.HasProperty(p => p.Status)
.EqualTo(ProductStatus.Active)
.Or.EqualTo(ProductStatus.Pending)
.And.HasProperty(p => p.Price)
.GreaterThan(0)
.And.LessThan(10000);

// Multiple condition paths
await Assert.That(product.Category)
.IsEqualTo("Electronics")
.And(Assert.That(product.Warranty).IsNotNull())
.Or
.IsEqualTo("Books")
.And(Assert.That(product.ISBN).IsNotNull());
}
```

### Performance Assertions

```csharp
[Test]
public async Task PerformanceAssertions()
{
var stopwatch = Stopwatch.StartNew();
var results = new List<long>();

// Measure multiple operations
for (int i = 0; i < 100; i++)
{
var start = stopwatch.ElapsedMilliseconds;
await PerformOperationAsync();
results.Add(stopwatch.ElapsedMilliseconds - start);
}

// Assert on performance metrics
await Assert.That(results.Average())
.IsLessThan(100); // Average under 100ms

await Assert.That(results.Max())
.IsLessThan(500); // No operation over 500ms

await Assert.That(results.Where(r => r > 200).Count())
.IsLessThan(5); // Less than 5% over 200ms
}
```

### State Machine Assertions

```csharp
[Test]
public async Task StateMachineAssertions()
{
var workflow = new OrderWorkflow();

// Initial state
await Assert.That(workflow.State).IsEqualTo(OrderState.New);

// State transition assertions
await workflow.StartProcessing();
await Assert.That(workflow.State)
.IsEqualTo(OrderState.Processing)
.And(Assert.That(workflow.CanTransitionTo(OrderState.Completed)).IsTrue())
.And(Assert.That(workflow.CanTransitionTo(OrderState.New)).IsFalse());

// Complex workflow validation
await workflow.Complete();
await Assert.That(workflow)
.HasProperty(w => w.State).EqualTo(OrderState.Completed)
.And.HasProperty(w => w.CompletedAt).IsNotNull()
.And.HasProperty(w => w.History).Contains(h => h.State == OrderState.Processing);
}
```

These examples demonstrate the power and flexibility of TUnit's assertion system, showing how you can build complex, readable assertions for various testing scenarios.
2 changes: 1 addition & 1 deletion docs/docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Your `.csproj` should be as simple as something like:
</PropertyGroup>

<ItemGroup>
<PackageReference Include="TUnit" Version="$(TUnitVersion)" />
<PackageReference Include="TUnit" Version="*" />
</ItemGroup>

</Project>
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/test-authoring/depends-on.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ e.g.:
```csharp
public void Test1(string value1, int value2) { ... }

[DependsOn(nameof(Test1), [typeof(string), typeof(int)])]
[DependsOn(nameof(Test1), new[] { typeof(string), typeof(int) })]
public void Test2() { ... }
```

Expand Down
Loading
Loading