{"id":34552453,"url":"https://github.com/jerrettdavis/tinybdd","last_synced_at":"2026-02-16T22:15:33.601Z","repository":{"id":309854261,"uuid":"1037767218","full_name":"JerrettDavis/TinyBDD","owner":"JerrettDavis","description":"TinyBDD is a minimal, fluent Behavior-Driven Development library for .NET","archived":false,"fork":false,"pushed_at":"2026-01-15T06:54:33.000Z","size":984,"stargazers_count":2,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-15T08:47:43.263Z","etag":null,"topics":["bdd","bdd-framework","bdd-tests","fluent","fluent-api","fluent-design"],"latest_commit_sha":null,"homepage":"https://jerrettdavis.github.io/TinyBDD/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JerrettDavis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-14T05:04:42.000Z","updated_at":"2026-01-15T04:14:55.000Z","dependencies_parsed_at":"2025-08-14T07:16:53.239Z","dependency_job_id":"670c8d32-3ed9-4430-ad6a-8f31046cbaa3","html_url":"https://github.com/JerrettDavis/TinyBDD","commit_stats":null,"previous_names":["jerrettdavis/tinybdd"],"tags_count":37,"template":false,"template_full_name":null,"purl":"pkg:github/JerrettDavis/TinyBDD","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JerrettDavis%2FTinyBDD","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JerrettDavis%2FTinyBDD/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JerrettDavis%2FTinyBDD/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JerrettDavis%2FTinyBDD/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JerrettDavis","download_url":"https://codeload.github.com/JerrettDavis/TinyBDD/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JerrettDavis%2FTinyBDD/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28597985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T02:08:49.799Z","status":"ssl_error","status_checked_at":"2026-01-20T02:08:44.148Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bdd","bdd-framework","bdd-tests","fluent","fluent-api","fluent-design"],"created_at":"2025-12-24T08:01:35.946Z","updated_at":"2026-02-16T22:15:33.580Z","avatar_url":"https://github.com/JerrettDavis.png","language":"C#","readme":"# TinyBDD\n\n[![CI](https://github.com/JerrettDavis/TinyBDD/actions/workflows/ci.yml/badge.svg)](https://github.com/JerrettDavis/TinyBDD/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/JerrettDavis/TinyBDD/branch/main/graph/badge.svg)](https://codecov.io/gh/JerrettDavis/TinyBDD)\n[![CodeQL](https://github.com/JerrettDavis/TinyBDD/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/JerrettDavis/TinyBDD/security/code-scanning)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md)\n![.NET Versions](https://img.shields.io/badge/.NET%208.0%20%7C%209.0-blue)\n\n**NuGet Packages:**\n\n| Package | Version | Downloads |\n|--------|---------|-----------|\n| **TinyBDD** | [![NuGet](https://img.shields.io/nuget/v/TinyBDD.svg)](https://www.nuget.org/packages/TinyBDD/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/TinyBDD.svg)](https://www.nuget.org/packages/TinyBDD/) |\n| **TinyBDD.MSTest** | [![NuGet](https://img.shields.io/nuget/v/TinyBDD.MSTest.svg)](https://www.nuget.org/packages/TinyBDD.MSTest/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/TinyBDD.MSTest.svg)](https://www.nuget.org/packages/TinyBDD.MSTest/) |\n| **TinyBDD.Xunit** | [![NuGet](https://img.shields.io/nuget/v/TinyBDD.Xunit.svg)](https://www.nuget.org/packages/TinyBDD.Xunit/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/TinyBDD.Xunit.svg)](https://www.nuget.org/packages/TinyBDD.Xunit/) |\n| **TinyBDD.Xunit.v3** | [![NuGet](https://img.shields.io/nuget/v/TinyBDD.Xunit.v3.svg)](https://www.nuget.org/packages/TinyBDD.Xunit.v3/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/TinyBDD.Xunit.v3.svg)](https://www.nuget.org/packages/TinyBDD.Xunit.v3/) |\n| **TinyBDD.NUnit** | [![NuGet](https://img.shields.io/nuget/v/TinyBDD.NUnit.svg)](https://www.nuget.org/packages/TinyBDD.NUnit/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/TinyBDD.NUnit.svg)](https://www.nuget.org/packages/TinyBDD.NUnit/) |\n| **TinyBDD.Extensions.DependencyInjection** | [![NuGet](https://img.shields.io/nuget/v/TinyBDD.Extensions.DependencyInjection.svg)](https://www.nuget.org/packages/TinyBDD.Extensions.DependencyInjection/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/TinyBDD.Extensions.DependencyInjection.svg)](https://www.nuget.org/packages/TinyBDD.Extensions.DependencyInjection/) |\n| **TinyBDD.Extensions.FileBased** | [![NuGet](https://img.shields.io/nuget/v/TinyBDD.Extensions.FileBased.svg)](https://www.nuget.org/packages/TinyBDD.Extensions.FileBased/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/TinyBDD.Extensions.FileBased.svg)](https://www.nuget.org/packages/TinyBDD.Extensions.FileBased/) |\n| **TinyBDD.Extensions.Hosting** | [![NuGet](https://img.shields.io/nuget/v/TinyBDD.Extensions.Hosting.svg)](https://www.nuget.org/packages/TinyBDD.Extensions.Hosting/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/TinyBDD.Extensions.Hosting.svg)](https://www.nuget.org/packages/TinyBDD.Extensions.Hosting/) |\n| **TinyBDD.Extensions.Reporting** | [![NuGet](https://img.shields.io/nuget/v/TinyBDD.Extensions.Reporting.svg)](https://www.nuget.org/packages/TinyBDD.Extensions.Reporting/) | [![NuGet Downloads](https://img.shields.io/nuget/dt/TinyBDD.Extensions.Reporting.svg)](https://www.nuget.org/packages/TinyBDD.Extensions.Reporting/) |\n\n---\n\n**TinyBDD** is a minimal, fluent **Behavior-Driven Development** library for .NET.\nIt supports two complementary approaches:\n- **Code-first**: Fluent `Given` / `When` / `Then` syntax directly in C#\n- **File-based**: Gherkin `.feature` files and YAML scenarios with convention-based drivers\n\nBoth approaches are designed to:\n\n- Be **framework-agnostic** (works with MSTest, xUnit, NUnit, etc.).\n- Keep scenarios **clear and concise** with readable syntax.\n- Support **async and sync** operations for maximum flexibility.\n- Integrate with existing test runners' output for easy step visibility.\n\n\n---\n\n## Features\n\n### Code-First Approach\n\n- **Readable BDD syntax**:\n  ```csharp\n    await Given(\"a number\", () =\u003e 5)\n        .When(\"doubled\", x =\u003e x * 2)\n        .Then(\"\u003e= 10\", v =\u003e v \u003e= 10)\n        .And(\"\u003c= 20\", v =\u003e v \u003c= 20)\n        .But(\"!= 15\", v =\u003e v != 15)\n        .AssertPassed();\n  ```\n- **Sync \u0026 Async Support**:\n\n    * `Func\u003cT\u003e` / `Func\u003cT, bool\u003e`\n    * `Func\u003cTask\u003cT\u003e\u003e` / `Func\u003cT, Task\u003cbool\u003e\u003e`\n    * Token-aware variants for advanced control.\n\n- **`And` / `But` chaining** with correct step names in output:\n\n  ```\n  Given start [OK]\n  When double [OK]\n  Then \u003e= 10 [OK]\n  And \u003c= 20 (async) [OK]\n  But != 11 [OK]\n  ```\n\n### File-Based Approach\n\n- **Gherkin .feature files**:\n  ```gherkin\n  Feature: Calculator Operations\n\n  Scenario: Add two numbers\n    Given a calculator\n    When I add 5 and 3\n    Then the result should be 8\n  ```\n\n- **Convention-based driver methods**:\n  ```csharp\n  [DriverMethod(\"I add {a} and {b}\")]\n  public Task Add(int a, int b)\n  {\n      _calculator.Add(a, b);\n      return Task.CompletedTask;\n  }\n  ```\n\n- **Scenario Outlines** with Examples tables for parameterized tests\n- **YAML format** as alternative to Gherkin for tooling integration\n\n### Framework Integration\n\n- **Test framework adapters**:\n\n    * **MSTest**: `TinyBddMsTestBase`, `MSTestBddReporter`, `MSTestTraitBridge`\n    * **xUnit**:  `TinyBddXunitBase`, `XunitTraitBridge`, `XunitBddReporter`\n    * **NUnit**: `TinyBddNUnitBase`, `NUnitTraitBridge`, `NUnitBddReporter`\n    * Automatically logs steps and tags to the test output.\n\n---\n\n## Installation\n\nAdd TinyBDD via NuGet:\n\n```powershell\ndotnet add package TinyBDD\n```\n\nFor MSTest:\n\n```powershell\ndotnet add package TinyBDD.MSTest\n```\n\nFor NUnit:\n\n```powershell\ndotnet add package TinyBDD.NUnit\n```\n\nFor xUnit:\n\n```powershell\ndotnet add package TinyBDD.Xunit\n```\n\nFor xUnit v3:\n\n```powershell\ndotnet add package TinyBDD.Xunit.v3\n```\n\nFor Extensions:\n\n```powershell\n# File-Based DSL (Gherkin and YAML)\ndotnet add package TinyBDD.Extensions.FileBased\n\n# Dependency Injection\ndotnet add package TinyBDD.Extensions.DependencyInjection\n\n# Hosting (includes DI)\ndotnet add package TinyBDD.Extensions.Hosting\n\n# JSON Reporting\ndotnet add package TinyBDD.Extensions.Reporting\n```\n\n### ⚡ Performance Optimization (Automatic!)\n\nTinyBDD includes a **Roslyn source generator** that **automatically optimizes ALL BDD tests at compile-time** starting in v1.1. No attributes needed, no configuration, no additional packages!\n\n\u003e **📌 Important:** Test classes **must be declared as `partial`** to enable source generation. This allows the generator to add optimized methods to your test class.\n\n**Default behavior** - All BDD test methods are automatically optimized:\n\n```csharp\npublic partial class MyTests  // ← Note: 'partial' keyword required\n{\n    // This is automatically optimized - no attribute needed!\n    public async Task FastScenario()\n    {\n        await Given(\"start\", () =\u003e 42)\n             .When(\"double\", x =\u003e x * 2)\n             .Then(\"equals 84\", x =\u003e x == 84);\n    }\n}\n```\n\n**Opt-out** - Use `[DisableOptimization]` if you need the full pipeline features:\n\n```csharp\n[DisableOptimization]  // Uses standard pipeline\npublic async Task ScenarioWithObservers()\n{\n    // Uses standard pipeline (observers, hooks, etc.)\n    await Given(\"start\", () =\u003e 1)\n         .When(\"add\", x =\u003e x + 1)\n         .Then(\"equals 2\", x =\u003e x == 2);\n}\n```\n\n**Performance gains:**\n- **16-40x faster execution** (~814ns → ~20-50ns per step)\n- **9x less memory** (2,568 bytes → ~290 bytes per scenario)\n- **Zero boxing** - All values are strongly typed at compile-time\n- **No runtime overhead** - Transforms to direct procedural code\n- **Compile-time transformation** - Happens automatically during build\n\n**When to opt-out:**\n- ⚠️ Using IStepObserver or IScenarioObserver (not yet supported in generated code)\n- ⚠️ Using BeforeStep/AfterStep hooks\n- ⚠️ Complex ScenarioOptions features\n- 🐛 Debugging (to step through standard pipeline)\n\n**Common warnings:**\n- **TBDD010**: Test class is not `partial` → Add `partial` keyword to your class declaration\n- **TBDD011**: Nested types not supported → Move tests to a top-level `partial` class\n- **TBDD012**: Generic types not supported → Use non-generic `partial` class for tests\n\nThe generator transforms fluent chains into optimized procedural code while maintaining the same readable syntax. Generated code is placed in `obj/.../generated/` for inspection.\n\n---\n\n## Basic Usage\n\n### MSTest Example\n\n```csharp\nusing TinyBDD.MSTest;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\n[Feature(\"Math\")]\n[TestClass]\npublic class MathTests : TinyBddMsTestBase\n{\n    [Scenario(\"Doubling numbers\")]\n    [TestMethod]\n    public async Task DoublingScenario()\n    {\n        await Given(\"start with 5\", () =\u003e 5)\n             .When(\"doubled\", x =\u003e x * 2)\n             .Then(\"should be 10\", v =\u003e v == 10)\n             .AssertPassed();\n    }\n}\n```\n\n---\n\n### NUnit Example\n\n```csharp\nusing TinyBDD.NUnit;\nusing NUnit.Framework;\n\n[Feature(\"Math\")]\npublic class MathTests : TinyBddNUnitBase\n{\n    [Scenario(\"Doubling numbers\")]\n    [Test]\n    public async Task DoublingScenario()\n    {\n        await Given(\"start with 5\", () =\u003e 5)\n             .When(\"doubled\", x =\u003e x * 2)\n             .Then(\"should be 10\", v =\u003e v == 10)\n             .AssertPassed();\n    }\n}\n```\n\n---\n\n### xUnit Example\n\n```csharp\nusing TinyBDD.Xunit;\nusing Xunit;\n\n[Feature(\"Math\")]\npublic class MathTests : TinyBddXunitBase\n{\n    [Scenario(\"Doubling numbers\")]\n    [Fact]\n    public async Task DoublingScenario()\n    {\n        await Given(\"start with 5\", () =\u003e 5) \n             .When(\"doubled\", x =\u003e x * 2)\n             .Then(\"should be 10\", v =\u003e v == 10)\n             .AssertPassed();\n    }\n}\n```\n\n---\n\n## File-Based Usage\n\n### Gherkin Feature File\n\nCreate `Features/Calculator.feature`:\n\n```gherkin\nFeature: Calculator Operations\n\n@calculator @smoke\nScenario: Add two numbers\n  Given a calculator\n  When I add 5 and 3\n  Then the result should be 8\n\nScenario Outline: Multiply numbers\n  Given a calculator\n  When I multiply \u003ca\u003e and \u003cb\u003e\n  Then the result should be \u003cexpected\u003e\n\nExamples:\n  | a | b | expected |\n  | 2 | 3 | 6        |\n  | 4 | 5 | 20       |\n```\n\n### Driver Implementation\n\n```csharp\nusing TinyBDD.Extensions.FileBased.Core;\n\npublic class CalculatorDriver : IApplicationDriver\n{\n    private readonly Calculator _calculator = new();\n\n    [DriverMethod(\"a calculator\")]\n    public Task Initialize()\n    {\n        _calculator.Clear();\n        return Task.CompletedTask;\n    }\n\n    [DriverMethod(\"I add {a} and {b}\")]\n    public Task Add(int a, int b)\n    {\n        _calculator.Add(a, b);\n        return Task.CompletedTask;\n    }\n\n    [DriverMethod(\"I multiply {a} and {b}\")]\n    public Task Multiply(int a, int b)\n    {\n        _calculator.Multiply(a, b);\n        return Task.CompletedTask;\n    }\n\n    [DriverMethod(\"the result should be {expected}\")]\n    public Task\u003cbool\u003e VerifyResult(int expected)\n    {\n        return Task.FromResult(_calculator.GetResult() == expected);\n    }\n\n    public Task InitializeAsync(CancellationToken ct = default) =\u003e Task.CompletedTask;\n    public Task CleanupAsync(CancellationToken ct = default) =\u003e Task.CompletedTask;\n}\n```\n\n### Test Class\n\n```csharp\nusing TinyBDD.Extensions.FileBased;\n\npublic class CalculatorTests : FileBasedTestBase\u003cCalculatorDriver\u003e\n{\n    [Fact]\n    public async Task ExecuteCalculatorScenarios()\n    {\n        await ExecuteScenariosAsync(options =\u003e\n        {\n            options.AddFeatureFiles(\"Features/**/*.feature\")\n                   .WithBaseDirectory(Directory.GetCurrentDirectory());\n        });\n    }\n}\n```\n\nOutput:\n\n```\nFeature: Calculator Operations\nScenario: Add two numbers\n  Given a calculator [OK] 0 ms\n  When I add 5 and 3 [OK] 0 ms\n  Then the result should be 8 [OK] 1 ms\n\nScenario Outline: Multiply numbers (Example 1: a=2, b=3, expected=6)\n  Given a calculator [OK] 0 ms\n  When I multiply 2 and 3 [OK] 0 ms\n  Then the result should be 6 [OK] 0 ms\n```\n\n---\n\n## Step Types\n\n| Step    | Purpose                                     | Example                        |\n|---------|---------------------------------------------|--------------------------------|\n| `Given` | Initial state / setup                       | `.Given(\"start\", () =\u003e 5)`     |\n| `When`  | Action / event                              | `.When(\"doubled\", x =\u003e x * 2)` |\n| `Then`  | Assertion                                   | `.Then(\"\u003e= 10\", v =\u003e v \u003e= 10)` |\n| `And`   | Additional assertion after `Then` or `When` | `.And(\"\u003c= 20\", v =\u003e v \u003c= 20)`  |\n| `But`   | Additional assertion phrased negatively     | `.But(\"!= 15\", v =\u003e v != 15)`  |\n\nAll step types have **sync** and **async** overloads.\n\n---\n\n## Cleanup with Finally\n\n`Finally` registers cleanup handlers that execute after all steps complete, even if steps throw exceptions. This is useful for resource cleanup like disposing objects:\n\n```csharp\nawait Given(\"a database connection\", () =\u003e new SqlConnection(connectionString))\n    .Finally(\"close connection\", conn =\u003e conn.Dispose())\n    .When(\"query data\", conn =\u003e conn.Query\u003cUser\u003e(\"SELECT * FROM Users\"))\n    .Then(\"results returned\", users =\u003e users.Any())\n    .AssertPassed();\n\n// Connection is automatically disposed after all steps complete\n```\n\n**Key Features:**\n- Finally handlers execute in registration order after all other steps\n- They execute even when steps throw exceptions\n- Multiple Finally handlers can be registered at different points in the chain\n- Each Finally handler receives the state value at the point where it was registered\n- The chain passes through the upstream value unchanged (tap semantics)\n\n```csharp\nawait Given(\"resource A\", () =\u003e new ResourceA())\n    .Finally(\"cleanup A\", a =\u003e a.Dispose())\n    .When(\"create resource B\", a =\u003e new ResourceB(a))\n    .Finally(\"cleanup B\", b =\u003e b.Dispose())\n    .Then(\"verify\", b =\u003e b.IsValid)\n    .AssertPassed();\n\n// Execution order: Given → When → Then → Finally cleanup A → Finally cleanup B\n```\n\n---\n\n## Tags\n\nTags can be added for reporting and filtering:\n\n```csharp\nctx.AddTag(\"smoke\");\nctx.AddTag(\"fast\");\n```\n\nIn xUnit, tags are logged to the test output:\n\n```\n[TinyBDD] Tag: smoke\n[TinyBDD] Tag: fast\n```\n\n---\n\n## Asserting Pass/Fail\n\nTinyBDD tracks step results internally. At the end of the scenario, call one of the following methods:\n\n```csharp\nScenario.AssertPassed();\n\nScenario.AssertFailed();\n\n// or use the fluent syntax:\nawait Given(\"one\", () =\u003e 1)\n    .When(\"add one\", x =\u003e x + 1)\n    .Then(\"equals two\", v =\u003e v == 2)\n    .AssertPassed();\n\nawait Given(\"one\", () =\u003e 1)\n    .When(\"add one\", x =\u003e x + 1)\n    .Then(\"equals elevent\", v =\u003e v == 11)\n    .AssertFailed();\n```\n\nThis ensures that all steps passed and throws if any failed.\n\n---\n\n## Philosophy\n\nTinyBDD was created with a few guiding principles:\n\n1. **Focus on readability, not ceremony**\n   Steps should read like plain English and map directly to Gherkin-style thinking. Choose the approach that best fits your team:\n   - **Code-first**: Write BDD tests directly in C# with fluent API\n   - **File-based**: Use standard Gherkin `.feature` files or YAML for business-readable specifications\n\n2. **Flexible specification approach**\n   Both approaches produce the same readable output:\n   - **Code-first**: Your C# code using the fluent API serves as the executable specification\n   - **File-based**: Separate `.feature` or YAML files define scenarios, implemented through driver methods\n\n   Your test runner output **is** the human-readable spec in both cases.\n\n3. **Stay out of your way**\n   TinyBDD is not an opinionated test framework; it's a syntax layer that integrates with MSTest, xUnit, or NUnit and\n   leaves assertions, test discovery, and reporting to them. Choose code-first for flexibility and complex logic, or\n   file-based when business analysts need to author test specifications.\n\n---\n\n## Gherkin-Style Output\n\nWhen running a scenario, TinyBDD prints structured step output similar to Gherkin formatting.  \nFor example:\n\n```csharp\nawait Given(\"start\", () =\u003e 5)\n    .When(\"double\", x =\u003e x * 2)\n    .Then(\"\u003e= 10\", v =\u003e v \u003e= 10)\n    .And(\"\u003c= 20 (async)\", v =\u003e Task.FromResult(v \u003c= 20))\n    .But(\"!= 11\", v =\u003e v != 11)\n    .AssertPassed();\n```\n\nTest output:\n\n```\nFeature: Math\nScenario: Doubling numbers\n  Given start [OK] 0 ms\n  When double [OK] 0 ms\n  Then \u003e= 10 [OK] 0 ms\n  And \u003c= 20 (async) [OK] 0 ms\n  But != 11 [OK] 0 ms\n```\n\nIf a step fails, you’ll see exactly which step failed, how long it took, and the exception message.\n\n---\n\n## Why Use TinyBDD?\n\nTinyBDD offers flexibility that traditional BDD tools don't:\n\n**Compared to SpecFlow / Cucumber:**\n* **Choose your approach**: Start code-first, add `.feature` files later when business analysts join, or vice versa\n* **No runtime overhead**: File-based DSL uses convention-based matching without reflection or runtime parsing\n* **Lighter setup**: Works directly with standard test frameworks (xUnit, NUnit, MSTest)\n* **Better IDE support**: Code-first approach gets full IntelliSense, refactoring, and debugging\n* **Simpler integration**: No separate test runners, no complex step binding configurations\n\n**Unique advantages:**\n* Seamlessly switch between approaches as your team's needs evolve\n* Both approaches produce identical readable Gherkin-style output\n* Test framework agnostic - use the test runner you already know\n* Performance-optimized with automatic source generation (code-first)\n* Convention-based driver methods for file-based tests (no attribute soup)\n\n---\n\n## Minimal Example\n\nFor the smallest possible test:\n\n```csharp\nawait Given(\"one\", () =\u003e 1)\n    .When(\"add one\", x =\u003e x + 1)\n    .Then(\"equals two\", v =\u003e v == 2)\n    .AssertPassed();\n```\n\nOutput:\n\n```\nGiven one [OK]\nWhen add one [OK]\nThen equals two [OK]\n```\n\n---\n\n## Async Philosophy\n\nIn TinyBDD, sync and async steps are **equally first-class** citizens.\n\n* If your step is synchronous, write it synchronously:\n\n  ```csharp\n  .When(\"double\", x =\u003e x * 2)\n  .Then(\"is 10\", v =\u003e v == 10)\n  ```\n\n* If your step needs async work:\n\n  ```csharp\n  .When(\"fetch from DB\", async x =\u003e await db.GetAsync(x))\n  .Then(\"result exists\", async v =\u003e Assert.NotNull(v))\n  ```\n\nYou can even mix sync and async steps freely in the same scenario.\n\n---\n\n## Output Style\n\nTinyBDD always prints the BDD keyword for the step type (`Given`, `When`, `Then`, `And`, `But`), the step title, the\nresult `[OK]` / `[FAIL]`, and the elapsed time in milliseconds.\n\nFor failed steps, TinyBDD stops the scenario immediately and prints the exception:\n\n```\nThen equals two [FAIL] 1 ms\nExpected: 2\nActual:   3\n```\n\n---\n\n## Recommended Usage\n\n* One **scenario** per test method.\n* Keep each step **single-purpose**—avoid hiding multiple unrelated actions in one step.\n* Prefer creating functions, even local ones, to avoid unnecessary allocations, closure creation, garbage collection, and code cleanliness.\n* Use **`Scenario.AssertPassed()`** or the fluent **`ThenChain.AssertPassed()`**  at the end of each test to ensure every step was explicitly checked.\n* Use **tags** to group and filter tests.\n\n----\n\n## License\n\n[MIT License](LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjerrettdavis%2Ftinybdd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjerrettdavis%2Ftinybdd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjerrettdavis%2Ftinybdd/lists"}