# Unit testing

Unit-test your data-access code with no database by mocking the generated context interfaces — ISocigyDatabaseFactory, I{Db} and I{Table}Set.

## The mockable seam

Because the [database context](/database/0.3.1/core-concepts/database-context) is entirely interface-based, services that depend on `ISocigyDatabaseFactory<I{Db}>` can be unit-tested with no database. You mock three things:

- `ISocigyDatabaseFactory<IAuthDb>` — and make `ExecuteAsync`/`ExecuteTransactionAsync` invoke the delegate inline,
- `IAuthDb` — return mocked table sets,
- `IUserSet` (etc.) — return canned data / verify calls.

## Example (Moq)

The service under test depends only on the interface:

```csharp
public class EnrollmentService(ISocigyDatabaseFactory<IAuthDb> db)
{
    public Task<Guid> EnrollAsync(string courseName) =>
        db.ExecuteTransactionAsync(async d =>
        {
            var course = new Course { Id = Guid.NewGuid(), Name = courseName };
            await d.Courses.InsertAsync(course);
            return course.Id;
        });
}
```

The test needs no PostgreSQL:

```csharp
[Test]
public async Task EnrollAsync_inserts_course()
{
    var courses = new Mock<ICourseSet>();
    courses.Setup(s => s.InsertAsync(It.IsAny<Course>())).ReturnsAsync(true);

    var ctx = new Mock<IAuthDb>();
    ctx.SetupGet(c => c.Courses).Returns(courses.Object);

    var factory = new Mock<ISocigyDatabaseFactory<IAuthDb>>();
    factory
        .Setup(f => f.ExecuteTransactionAsync(It.IsAny<Func<IAuthDb, Task<Guid>>>(), It.IsAny<CancellationToken>()))
        .Returns((Func<IAuthDb, Task<Guid>> work, CancellationToken _) => work(ctx.Object)); // run inline

    var sut = new EnrollmentService(factory.Object);
    var id = await sut.EnrollAsync("Intro");

    Assert.That(id, Is.Not.EqualTo(Guid.Empty));
    courses.Verify(s => s.InsertAsync(It.Is<Course>(c => c.Name == "Intro")), Times.Once);
}
```

The key trick is the `.Returns(...)` that **invokes the delegate** with the mocked context, so the lambda body actually runs and your `InsertAsync` expectations are exercised.

## Integration tests

For end-to-end coverage against a real database, resolve the factory from a real `ServiceCollection` (with `AddAuthDb()` + `AddAuthDbContext()`) and assert on actual rows — including transaction commit/rollback. See the library's own `UnitTest.DB.Tests` for a working example.

> **NOTE** Keep `IAsyncEnumerable` streaming (`ForEachAsync`) out of pure unit tests — terminal methods (`ToListAsync`, `FirstOrDefaultAsync`, `ExistsAsync`, `CountAsync`) are the natural mocking surface.
