# Assertions Reference

Complete reference for TUnit assertions.

## Table of Contents

- [Core Principles](#core-principles)
- [Equality & Comparison](#equality--comparison)
- [Null & Default](#null--default)
- [Boolean](#boolean)
- [Numeric](#numeric)
- [String](#string)
- [Collections](#collections)
- [HTTP Responses](#http-responses)
- [Exceptions](#exceptions)
- [Type Assertions](#type-assertions)
- [Chaining & Composition](#chaining--composition)

## Core Principles

All assertions:
- Use `Assert.That(value)` syntax
- Must be `await`ed to execute
- Return fluent builder objects for chaining
- Provide clear error messages on failure

```csharp
// Always await assertions
await Assert.That(actual).IsEqualTo(expected);

// Chaining
await Assert.That(value)
    .IsNotNull()
    .And.IsGreaterThan(0);
```

## Equality & Comparison

### Equality

```csharp
await Assert.That(actual).IsEqualTo(expected);
await Assert.That(actual).IsNotEqualTo(other);
await Assert.That(actual).IsSameReference(expected);
await Assert.That(actual).IsNotSameReference(other);
```

### Comparison

```csharp
await Assert.That(value).IsGreaterThan(10);
await Assert.That(value).IsGreaterThanOrEqualTo(10);
await Assert.That(value).IsLessThan(100);
await Assert.That(value).IsLessThanOrEqualTo(100);
await Assert.That(value).IsBetween(10, 100);  // Inclusive
```

### With Tolerance

For floating-point comparisons:

```csharp
await Assert.That(3.14159).IsEqualTo(Math.PI).Within(0.001);
await Assert.That(actual).IsCloseTo(expected, tolerance: 0.01);
```

## Null & Default

```csharp
await Assert.That(value).IsNull();
await Assert.That(value).IsNotNull();
await Assert.That(value).IsDefault();      // default(T)
await Assert.That(value).IsNotDefault();
```

## Boolean

```csharp
await Assert.That(condition).IsTrue();
await Assert.That(condition).IsFalse();
```

## Numeric

All comparison assertions work with numeric types:

```csharp
await Assert.That(42).IsPositive();
await Assert.That(-10).IsNegative();
await Assert.That(0).IsZero();
await Assert.That(5).IsOdd();
await Assert.That(4).IsEven();
```

## String

### Content

```csharp
await Assert.That(str).IsEmpty();
await Assert.That(str).IsNotEmpty();
await Assert.That(str).Contains("substring");
await Assert.That(str).DoesNotContain("substring");
await Assert.That(str).StartsWith("prefix");
await Assert.That(str).EndsWith("suffix");
```

### Pattern Matching

```csharp
await Assert.That(email).Matches(@"^[\w\.-]+@[\w\.-]+\.\w+$");
await Assert.That(text).DoesNotMatch(@"\d+");  // No digits
```

### Length

```csharp
await Assert.That(str).Length().IsEqualTo(10);
await Assert.That(str).Length().IsGreaterThan(5);
```

### Case Sensitivity

```csharp
// Case-insensitive comparison
await Assert.That("Hello").IsEqualTo("hello", StringComparison.OrdinalIgnoreCase);
```

## Collections

### Basic Checks

```csharp
await Assert.That(collection).IsEmpty();
await Assert.That(collection).IsNotEmpty();
await Assert.That(collection).Count().IsEqualTo(5);
await Assert.That(collection).Count().IsGreaterThan(0);
```

### Membership

```csharp
await Assert.That(collection).Contains(item);
await Assert.That(collection).DoesNotContain(item);
await Assert.That(collection).ContainsAll(item1, item2, item3);
await Assert.That(collection).ContainsAny(item1, item2);
```

### Predicates

```csharp
await Assert.That(numbers).All(n => n > 0);
await Assert.That(numbers).Any(n => n == 42);
await Assert.That(numbers).None(n => n < 0);
```

### Single Item

```csharp
// Returns the single item if count == 1
var item = await Assert.That(collection).HasSingleItem();
await Assert.That(item.Name).IsEqualTo("Expected");
```

### Contains with Predicate

```csharp
// Returns the found item
var admin = await Assert.That(users).Contains(u => u.Role == "Admin");
await Assert.That(admin.Permissions).IsNotEmpty();
```

### Ordering

```csharp
await Assert.That(numbers).IsInOrder();
await Assert.That(numbers).IsInDescendingOrder();
```

### Equivalence

```csharp
// Same items, any order
await Assert.That(actual).IsEquivalentTo(expected);

// Exact order required
await Assert.That(actual).IsEqualTo(expected);
```

### Arrays And Buffers

For `byte[]` and similar payload buffers, prefer `IsEquivalentTo(...)` when you mean element-by-element equality of contents.

```csharp
await Assert.That(actualBytes).IsEquivalentTo(expectedBytes);
```

If a failure says the contents are the same but the reference is different, you likely want equivalence rather than direct equality.

## HTTP Responses

TUnit provides built-in HTTP response assertions:

### Specific Status Codes

```csharp
await Assert.That(response).IsOk();               // 200
await Assert.That(response).IsCreated();          // 201
await Assert.That(response).IsNoContent();        // 204
await Assert.That(response).IsBadRequest();       // 400
await Assert.That(response).IsUnauthorized();     // 401
await Assert.That(response).IsForbidden();        // 403
await Assert.That(response).IsNotFound();         // 404
await Assert.That(response).IsConflict();         // 409
```

### Parameterized

```csharp
await Assert.That(response).HasStatusCode(HttpStatusCode.Created);
await Assert.That(response).HasStatusCode(HttpStatusCode.Accepted);
```

### Range Checks

```csharp
await Assert.That(response).IsSuccessStatusCode();       // 200-299
await Assert.That(response).IsRedirectStatusCode();      // 300-399
await Assert.That(response).IsClientErrorStatusCode();   // 400-499
await Assert.That(response).IsServerErrorStatusCode();   // 500-599
```

### Content & Headers

```csharp
await Assert.That(response).HasJsonContent();
await Assert.That(response).HasContentType("application/json");
await Assert.That(response).HasHeader("X-Request-Id");
await Assert.That(response).HasHeader("Content-Type", "application/json");
```

### Complete Example

```csharp
var response = await client.PostAsJsonAsync("/alerts", request);

await Assert.That(response).IsCreated();
await Assert.That(response).HasJsonContent();
await Assert.That(response).HasHeader("Location");

var alert = await response.Content.ReadFromJsonAsync<AlertResponse>();
await Assert.That(alert).IsNotNull();
await Assert.That(alert.Name).IsEqualTo(request.Name);
```

## Exceptions

### Basic Exception Testing

```csharp
await Assert.That(() => DivideByZero())
    .Throws<DivideByZeroException>();

await Assert.That(async () => await service.ProcessAsync())
    .Throws<InvalidOperationException>();
```

### With Message Validation

```csharp
await Assert.That(() => ThrowException())
    .Throws<ArgumentException>()
    .WithMessage("Parameter name: value");

await Assert.That(() => ThrowException())
    .Throws<ArgumentException>()
    .WithMessageContaining("value");
```

### No Exception

```csharp
await Assert.That(() => SafeOperation()).ThrowsNothing();
await Assert.That(async () => await SafeAsync()).ThrowsNothing();
```

## Type Assertions

```csharp
await Assert.That(obj).IsTypeOf<MyClass>();
await Assert.That(obj).IsNotTypeOf<OtherClass>();
await Assert.That(typeof(Dog)).IsAssignableTo<Animal>();
await Assert.That(typeof(Dog)).IsAssignableFrom<Animal>();
```

## Chaining & Composition

### And Conditions

Chain assertions on the same value:

```csharp
await Assert.That(username)
    .IsNotNull()
    .And.IsNotEmpty()
    .And.Length().IsGreaterThan(3)
    .And.Length().IsLessThan(20);
```

### Or Conditions

Any condition can be true:

```csharp
await Assert.That(statusCode)
    .IsEqualTo(200)
    .Or.IsEqualTo(201)
    .Or.IsEqualTo(204);
```

### Member Assertions

Assert on object properties:

```csharp
await Assert.That(person)
    .Member(p => p.Name, name => name.IsEqualTo("Alice"))
    .And.Member(p => p.Age, age => age.IsGreaterThan(18));

// Nested properties
await Assert.That(order)
    .Member(o => o.Customer.Address.City, city => city.IsEqualTo("Seattle"));
```

### Assert.Multiple

Group related assertions to see all failures:

```csharp
using (Assert.Multiple())
{
    await Assert.That(user.FirstName).IsEqualTo("John");
    await Assert.That(user.LastName).IsEqualTo("Doe");
    await Assert.That(user.Age).IsGreaterThan(18);
    await Assert.That(user.Email).IsNotNull();
}
// All failures reported together
```

### Custom Expectations

```csharp
// Simple predicate
await Assert.That(value).Satisfies(v => v % 2 == 0, "Value must be even");

// Map and assert
await Assert.That(order)
    .Satisfies(o => o.Total, total => total > 100);
```

## Common Patterns

### Testing Validation Results

```csharp
var result = validator.Validate(input);

using (Assert.Multiple())
{
    await Assert.That(result.IsValid).IsFalse();
    await Assert.That(result.Errors).Count().IsEqualTo(2);
    await Assert.That(result.Errors).Any(e => e.PropertyName == "Email");
}
```

### Testing API Responses

```csharp
var response = await client.GetAsync("/users/123");

await Assert.That(response).IsOk();
await Assert.That(response).HasJsonContent();

var user = await response.Content.ReadFromJsonAsync<User>();

using (Assert.Multiple())
{
    await Assert.That(user).IsNotNull();
    await Assert.That(user.Id).IsEqualTo(123);
    await Assert.That(user.Name).IsNotEmpty();
    await Assert.That(user.Email).Matches(@"^[\w\.-]+@[\w\.-]+\.\w+$");
}
```

### Testing Collections with Conditions

```csharp
var activeUsers = await repository.GetActiveUsersAsync();

await Assert.That(activeUsers).IsNotEmpty();
await Assert.That(activeUsers).All(u => u.IsActive);
await Assert.That(activeUsers).Any(u => u.Role == "Admin");

var admin = await Assert.That(activeUsers).Contains(u => u.Role == "Admin");
await Assert.That(admin.Permissions).ContainsAll("Read", "Write", "Delete");
```

## Best Practices

1. **Use specific assertions** - `IsOk()` instead of `HasStatusCode(HttpStatusCode.OK)`
2. **Chain related assertions** - Use `.And` for multiple checks on same value
3. **Group with Assert.Multiple** - See all failures at once
4. **Return values from assertions** - Use `HasSingleItem()`, `Contains(predicate)` to continue asserting
5. **Always await** - Assertions must be awaited to execute
6. **One logical assertion per test** - Multiple technical assertions OK if testing same behavior
