Introduction
TinyBDD is a tiny, fluent BDD helper for .NET tests. It gives you a clean Given/When/Then style without tying you to a specific test framework.
Why TinyBDD
- Tiny: a small core you can read in minutes
- Fluent: expressive chains for Given, When, Then, And, But
- Pragmatic: first-class adapters for xUnit, NUnit, and MSTest, but works fine without them
- Deferred fluent expectations:
Expect.For/Thatlet you compose.Because()/.With()/ checks in any order and only throw when awaited - Built-in step lineage: every step’s input/output captured in
ScenarioContext.IOplusCurrentItempointer for post-mortem analysis
Core ideas
- Two entry points
- Explicit: create a ScenarioContext with Bdd.CreateContext and pass it around
- Ambient: set Ambient.Current (or inherit a TinyBDD base class) and use Flow
- Cross-framework: adapters only handle output and tag bridging; assertions and flow stay the same
- Minimal assertions: bring your favorite assertion library; TinyBDD only needs a bool predicate or action per step
What a scenario looks like
- Explicit (no base class required):
var ctx = Bdd.CreateContext(this); // reads [Feature]/[Scenario] and method/test attributes
await Bdd.Given(ctx, "numbers", () => new[]{1,2,3})
.When("sum", (arr, _) => Task.FromResult(arr.Sum()))
.Then("> 0", sum => sum > 0)
.AssertPassed();
- Ambient (inherit a base class or set Ambient.Current):
await Given(() => 1)
.When("double", x => x * 2)
.Then("== 2", v => v == 2)
.AssertPassed();
Attributes and tags
- [Feature] on a class gives a friendly feature name (and optional description)
- [Scenario] on a test method provides a friendly scenario name and optional tags
- [Tag] on classes or methods adds tags; adapters forward tags to your test framework’s trait/category output
Assertions made simple
- Action-based: Then("works", () => Task.CompletedTask)
- Predicate-based: Then("x == 2", v => v == 2) — throws BddAssertException on false
- Fluent deferred: await Expect.For(value, "subject").Because("why").With("hint").ToBe(expected);
- With or without CancellationToken, and available for And/But too
Reporting
- Base classes emit Gherkin-style output automatically to the framework’s test log
- Manual reporting: GherkinFormatter.Write(ctx, reporter) with an IBddReporter
Design notes
- Steps are recorded in ScenarioContext.Steps as they execute
- Per-step Input/Output captured (ScenarioContext.IO) and latest CurrentItem maintained
- Failures are captured and rethrown as BddStepException with the original exception as InnerException
- Async-friendly by default; chains are Task-based and CancellationToken-friendly where it helps
Where next
Getting Started
- Getting Started - Installation, basic usage, and quick starts
- Fundamentals (BDD & Gherkin) - Core BDD concepts and vocabulary
- BDD + TDD Workflow - Integrating BDD with TDD practices
Writing and Organizing Tests
- Writing Scenarios - Background steps, scenario outlines, and tagging
- Data and Tables - Parameterized steps and table-driven tests
- Expectations & Assertions - Assertion strategies and fluent expectations
- Step IO & State Tracking - Data lineage and state management
Test Execution and Infrastructure
- Hooks and Lifecycle - Setup, teardown, and resource management
- Running with Test Frameworks - xUnit, NUnit, MSTest setup and CLI
- Reporting - Built-in reporters and custom report generation
Extensions and Orchestration
- Extensions Overview - DI, Hosting, and Reporting integration packages
- Dependency Injection - IServiceCollection integration
- Hosting - Background services and hosted workflows
- Reporting Extension - JSON reporting and observer pattern
- Orchestrator Patterns - Advanced workflow patterns
- Enterprise Samples - Production-ready examples
Reference and Help
- Tips & Tricks - Best practices and optimization techniques
- Extensibility & Advanced - Customization and advanced patterns
- Troubleshooting & FAQ - Common issues and solutions
- Samples Index - Runnable examples and code snippets