A .NET tool for generating test code from specification files.
Inspired by SpecFlow and Gauge, but with a focus on simplicity and modern features. Ensure.Generator takes a lightweight approach by focusing solely on generating clean, typed test code from simple markdown specifications.
In today's rapidly evolving software landscape, acceptance tests and executable specifications are becoming increasingly crucial. They serve as living documentation that evolves with your codebase, ensuring that your tests always reflect the current business requirements. Learn more about why this approach is gaining traction in this detailed overview video.
dotnet tool install --global Ensure.Generator- Create a specification file (e.g.,
login.spec.md) in your test project - Run the generator:
# For C#
ensure csharp -s path/to/specs -o path/to/output -n YourNamespace
# For TypeScript
ensure typescript -s path/to/specs -o path/to/output
# Options:# -s, --specs : Path to specs directory (default: "Specs" for C#, "specs" for TypeScript)# -o, --output : Output directory for generated tests (default: "Generated" for C#, "tests" for TypeScript)# -n, --namespace : Namespace for generated C# tests (required for C#)# -p, --preserve-location : Generate test files in the same location as spec filesThe generator will:
- Recursively search for spec files in the specs directory and its subdirectories
- By default, generate all test files in the output directory with namespace
YourNamespace.Generated - With
-p, generate test files alongside their corresponding spec files - When using
-pwith C#, the namespace will reflect the exact folder structure (e.g., if your folders areFeatures/Auth/OAuth, the namespace will beYourNamespace.Features.Auth.OAuthwithout the.Generatedsuffix)
# Login Feature## Successful Login- Navigate to "/login"
- Enter "test@example.com" into "email" field
- Enter "password123" into "password" field
- Click "Sign In" button
- Verify text "Welcome back" is shown
## Invalid Credentials- Navigate to "/login"
- Enter "wrong@example.com" into "email" field
- Enter "wrongpass" into "password" field
- Click "Sign In" button
- Verify text "Invalid credentials" is shown# User Data Validation## Validate Multiple Users- Load test users
| Name | Age | Email ||-------|-----|-----------------|| John | 25 |john@email.com|| Alice | 30 |alice@email.com|- Validate all users
- Check validation resultsThe tool generates both test classes and step definitions in C# or TypeScript.
// Generated Steps Base ClasspublicabstractclassLoginFeatureStepsBase{/// <summary>/// Navigate to "/login"/// </summary>publicabstractTaskNavigateTo(stringparam1);/// <summary>/// Enter "test@example.com" into "email" field/// </summary>publicabstractTaskEnterIntoField(stringparam1,stringparam2);/// <summary>/// Click "Sign In" button/// </summary>publicabstractTaskClickButton(stringparam1);/// <summary>/// Verify text "Welcome back" is shown/// </summary>publicabstractTaskVerifyTextIsShown(stringparam1);}// Generated Tests Base ClasspublicabstractclassLoginFeatureTestsBase{protectedabstractLoginFeatureStepsBaseSteps{get;}[Fact]publicasyncTaskSuccessfulLogin(){awaitSteps.NavigateTo("/login");awaitSteps.EnterIntoField("test@example.com","email");awaitSteps.EnterIntoField("password123","password");awaitSteps.ClickButton("Sign In");awaitSteps.VerifyTextIsShown("Welcome back");}// ... other test methods}// Generated Steps Base ClassexportabstractclassLoginFeatureStepsBase{/** * Navigate to "/login" */abstractnavigateTo(param1: string): Promise<void>;/** * Enter "test@example.com" into "email" field */abstractenterIntoField(param1: string,param2: string): Promise<void>;/** * Click "Sign In" button */abstractclickButton(param1: string): Promise<void>;/** * Verify text "Welcome back" is shown */abstractverifyTextIsShown(param1: string): Promise<void>;}// Generated Tests Base ClassexportabstractclassLoginFeatureTestsBase{protectedabstractgetSteps(page: Page): LoginFeatureStepsBase;test('Successful Login',async({ page })=>{conststeps=this.getSteps(page);awaitsteps.navigateTo('/login');awaitsteps.enterIntoField('test@example.com','email');awaitsteps.enterIntoField('password123','password');awaitsteps.clickButton('Sign In');awaitsteps.verifyTextIsShown('Welcome back');});// ... other test methods}To implement the tests, create concrete classes that inherit from the generated base classes:
publicclassLoginFeatureTests:LoginFeatureTestsBase{protectedoverrideLoginFeatureStepsBaseSteps=>new();}publicclassLoginFeatureSteps:LoginFeatureStepsBase{publicoverrideasyncTaskNavigateTo(stringurl){// Your implementation here}publicoverrideasyncTaskEnterIntoField(stringtext,stringfield){// Your implementation here}publicoverrideasyncTaskClickButton(stringbutton){// Your implementation here}publicoverrideasyncTaskVerifyTextIsShown(stringtext){// Your implementation here}}classLoginFeatureStepsextendsLoginFeatureStepsBase{constructor(privatepage: Page){super();}asyncnavigateTo(url: string): Promise<void>{// Your implementation here}asyncenterIntoField(text: string,field: string): Promise<void>{// Your implementation here}asyncclickButton(button: string): Promise<void>{// Your implementation here}asyncverifyTextIsShown(text: string): Promise<void>{// Your implementation here}}exportclassLoginFeatureTestsextendsLoginFeatureTestsBase{protectedgetSteps(page: Page): LoginFeatureStepsBase{returnnewLoginFeatureSteps(page);}}- Generates test code from markdown specification files
- Supports both C# (xUnit) and TypeScript (Playwright) output
- Clean, typed step definitions
- Simple bullet-point style steps
- Table-driven test scenarios
- Automatic parameter extraction from quoted strings
- No Gherkin/Cucumber syntax - just plain English
- First-class Playwright support for TypeScript output
- Proper namespace handling for C# output with folder structure support
- Recursive spec file search in subdirectories
- Option to generate test files alongside spec files
This project is licensed under the MIT License - see the LICENSE file for details.