-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Zentient.Testing is a lightweight, opinionated testing toolkit for .NET 8 and .NET 9, designed to make scenarioβdriven tests and focused unitβstyle integrations easy to write, read, and maintain.
It provides:
- Scenario factories for arranging dependencies, executing code, and asserting results.
- A compact mock engine with a fluent
Given/ThenReturns/ThenThrowsDSL. - Result assertions for expressive, chainable verification.
- A minimal harness for perβtest dependency resolution.
Audience:
Maintainers, contributors, integration engineers, and advanced adopters.
Purpose:
Document the public API, design intent, and canonical usage patterns for Zentient.Testing, with runnable examples from the repositoryβs samples/Features tests.
| Section | Description |
|---|---|
| Getting Started | Create and run your first scenario test. |
| Concepts | Core mental model and terminology. |
| Architecture | Highβlevel component overview and extension points. |
| Usage Examples | Recipes for common patterns. |
| API Reference (alfa) | Public API snapshot for v0.1.0-alfa. |
| Release Notes / Roadmap | Whatβs in alfa and whatβs next. |
From 0.1.0βalfa.cs:
-
Scenario Factories β
TestScenario.For,TestScenario.ForHandler(sync/async) -
Scenario Interface β
ITestScenario<TInput, TResult>with:Arrange(Action<ITestHarnessBuilder>)ActAsync(TInput, CancellationToken)Assert(Action<IResultAssertions<TResult>>)
-
Harness Builder β
ITestHarnessBuilderwith:WithDependency<T>(instance)WithMock<T>(configure)WithMock<T>(configure, out IMockVerifier<T>)Replace<T>(instance)
-
Mocking β
IMockBuilder<T>andIMockVerifier<T> -
Assertions β
IResultAssertions<TResult>withNotBeNull,HaveValue,WithProperty,AndAlso
All examples below are taken from the samples/Features tests in the repository.
var scenario = TestScenario.ForHandler<SimpleHandler, string, string>(
(h, input, ct) => Task.FromResult(h.Handle(input))
);
scenario.Arrange(b => b.WithDependency<ISimpleService>(new SimpleServiceImpl()));
var result = await scenario.ActAsync("in");
Xunit.Assert.Equal("impl:in", result);using Zentient.Testing.Internal; // for It.IsAny<T>()
var scenario = TestScenario.ForHandler<SimpleHandler, string, string>(
(h, input, ct) => Task.FromResult(h.Handle(input))
);
scenario.Arrange(b => b.WithMock<ISimpleService>(mb =>
mb.Given(s => s.Do(It.IsAny<string>())).ThenReturns("mocked")
));
var result = await scenario.ActAsync("in");
Xunit.Assert.Equal("mocked", result);IMockVerifier<ISimpleService> verifier = null!;
var scenario = TestScenario.ForHandler<SimpleHandler, string, string>(
(h, input, ct) => Task.FromResult(h.Handle(input))
);
await scenario
.Arrange(b => b.WithMock<ISimpleService>(
mb => mb.Given(s => s.Do(It.IsAny<string>())).ThenReturns("done"),
out verifier
))
.ActAsync("hello");
verifier.ShouldHaveBeenCalled(nameof(ISimpleService.Do));
verifier.ShouldHaveBeenCalledTimes(nameof(ISimpleService.Do), 1);var scenario = TestScenario.ForHandler<AsyncHandler, string, string>(
(h, input, ct) => h.HandleAsync(input, ct)
);
scenario.Arrange(b => b.WithMock<IWorker>(mb =>
mb.Given(x => x.WorkAsync(It.IsAny<string>()))
.ThenReturns(Task.FromResult("ok"))
));
var result = await scenario.ActAsync("input");
Xunit.Assert.Equal("ok", result);var scenario = TestScenario.ForHandler<ResultHandler, int, ResultDto>(
(h, input, ct) => Task.FromResult(h.Handle(input))
);
scenario.Arrange(b => b.WithDependency<IResultProvider>(new ResultProvider()));
var result = await scenario.ActAsync(7);
scenario.Assert(a => {
a.NotBeNull();
a.AndAlso.WithProperty(r => r.Value, 7);
});| Factory Method | When to Use | Example |
|---|---|---|
For<TInput, TResult> |
You want full control over the act step and will manually resolve services from the harness. | (h, input, ct) => ... |
ForHandler<THandler, TInput, TResult> (async) |
You have a handler type with an async method and want the harness to resolve it automatically. | (handler, input, ct) => handler.ProcessAsync(input, ct) |
ForHandler<THandler, TInput, TResult> (sync) |
Same as above, but for synchronous methods. | (handler, input) => handler.Process(input) |
- Keep scenarios small and deterministic β one input, one primary assertion.
- Use
WithDependencyfor stubs/fakes,WithMockfor interaction verification. - Prefer keeping examples in
samples/so they compile against the repo and stay current.
- Getting Started β run your first scenario test.
-
Usage Examples β more recipes from
samples/Features. -
API Reference (alfa) β full public surface for
v0.1.0-alfa.
Licensed under the MIT License. See the LICENSE file for more details.
- Zentient.Testing Home (Wiki Main Page)
- Main Repository (GitHub)
- Report an Issue / Feature Request
- Contributing Guidelines
- Code of Conduct
- Latest Release Notes
- [Discussions]: Ask questions, share ideas, or showcase your projects.
Thank you for exploring the Zentient.Testing documentation! We hope it helps you build elegant, maintainable, and contributorβfriendly .NET testing solutions.
Last updated: 19 Sep 2025
π Zentient.Testing Docs
Quick links to all major sections of the documentation.
- Landing Page β What Zentient.Testing is and why it exists.
- Release Notes β Changes in the current version.
- Roadmap β Planned features and milestones.
- Concepts β Mental model and terminology.
- Architecture β Components, relationships, and flow.
- Architecture Diagram β Visual Mermaid diagram.
- Getting Started β Install and run your first scenario.
-
Usage Examples β Recipes from
samples/Features.
- API Reference (alfa) β Public API for v0.1.0βalfa.
- Adapters & Integration Points β (future) DI and mocking adapters.
- Diagnostics β (future) DI graph and validation tools.
- Contributing Guide β How to contribute.
- Testing Strategy β How Zentient.Testing is tested.
- Changelog β Full version history.
π‘ Tip: Use the Architecture page as your hub β it links to Getting Started, Usage Examples, API Reference, and Concepts.