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
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
- 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
- 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
- Read Getting Started to install and set up for your test framework
- Browse the API reference for details and overloads