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 "[email protected]" into "email" field
- Enter "password123" into "password" field
- Click "Sign In" button
- Verify text "Welcome back" is shown
## Invalid Credentials
- Navigate to "/login"
- Enter "[email protected]" 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 | [email protected] |
| Alice | 30 | [email protected] |
- Validate all users
- Check validation resultsThe tool generates both test classes and step definitions in C# or TypeScript.
// Generated Steps Base Class
public abstract class LoginFeatureStepsBase
{
/// <summary>
/// Navigate to "/login"
/// </summary>
public abstract Task NavigateTo(string param1);
/// <summary>
/// Enter "[email protected]" into "email" field
/// </summary>
public abstract Task EnterIntoField(string param1, string param2);
/// <summary>
/// Click "Sign In" button
/// </summary>
public abstract Task ClickButton(string param1);
/// <summary>
/// Verify text "Welcome back" is shown
/// </summary>
public abstract Task VerifyTextIsShown(string param1);
}
// Generated Tests Base Class
public abstract class LoginFeatureTestsBase
{
protected abstract LoginFeatureStepsBase Steps { get; }
[Fact]
public async Task SuccessfulLogin()
{
await Steps.NavigateTo("/login");
await Steps.EnterIntoField("[email protected]", "email");
await Steps.EnterIntoField("password123", "password");
await Steps.ClickButton("Sign In");
await Steps.VerifyTextIsShown("Welcome back");
}
// ... other test methods
}// Generated Steps Base Class
export abstract class LoginFeatureStepsBase {
/**
* Navigate to "/login"
*/
abstract navigateTo(param1: string): Promise<void>;
/**
* Enter "[email protected]" into "email" field
*/
abstract enterIntoField(param1: string, param2: string): Promise<void>;
/**
* Click "Sign In" button
*/
abstract clickButton(param1: string): Promise<void>;
/**
* Verify text "Welcome back" is shown
*/
abstract verifyTextIsShown(param1: string): Promise<void>;
}
// Generated Tests Base Class
export abstract class LoginFeatureTestsBase {
protected abstract getSteps(page: Page): LoginFeatureStepsBase;
test('Successful Login', async ({ page }) => {
const steps = this.getSteps(page);
await steps.navigateTo('/login');
await steps.enterIntoField('[email protected]', 'email');
await steps.enterIntoField('password123', 'password');
await steps.clickButton('Sign In');
await steps.verifyTextIsShown('Welcome back');
});
// ... other test methods
}To implement the tests, create concrete classes that inherit from the generated base classes:
public class LoginFeatureTests : LoginFeatureTestsBase
{
protected override LoginFeatureStepsBase Steps => new();
}
public class LoginFeatureSteps : LoginFeatureStepsBase
{
public override async Task NavigateTo(string url)
{
// Your implementation here
}
public override async Task EnterIntoField(string text, string field)
{
// Your implementation here
}
public override async Task ClickButton(string button)
{
// Your implementation here
}
public override async Task VerifyTextIsShown(string text)
{
// Your implementation here
}
}class LoginFeatureSteps extends LoginFeatureStepsBase {
constructor(private page: Page) {
super();
}
async navigateTo(url: string): Promise<void> {
// Your implementation here
}
async enterIntoField(text: string, field: string): Promise<void> {
// Your implementation here
}
async clickButton(button: string): Promise<void> {
// Your implementation here
}
async verifyTextIsShown(text: string): Promise<void> {
// Your implementation here
}
}
export class LoginFeatureTests extends LoginFeatureTestsBase {
protected getSteps(page: Page): LoginFeatureStepsBase {
return new LoginFeatureSteps(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.