Your first test
This walkthrough builds a complete scenario with TUnit. It uses an in-memory account, so it needs no server or credentials.
Using another framework? The code is the same apart from the base class, the test attributes and the assertions. See Test frameworks.
0. Install the packages
dotnet add package TUnit
dotnet add package BDTest.TUnit
TUnit brings the test framework, assertions and runner; BDTest.TUnit only depends on TUnit.Core. The source generator is what turns Account.CreateAnAccount(100m) into "I have an account with a balance of 100" in the report. See Installation for a full project file.
Use an executable test project (<OutputType>Exe</OutputType>) with implicit usings enabled, as shown in the project file. The examples use types such as Task and File from those usings.
1. Create a context
A context holds the state for one test. BDTest creates a new instance for each test, so tests never share state.
public class AccountContext
{
public decimal Balance { get; set; }
}
The context class needs a public parameterless constructor. See Test context.
2. Write step classes
Steps are ordinary methods. Group them into classes that receive the context in their constructor. [StepText] controls how each step appears in the report; {openingBalance} is replaced by the argument for that parameter.
using BDTest;
public class AccountSteps
{
private readonly AccountContext _context;
public AccountSteps(AccountContext context) => _context = context;
[StepText("I have an account with a balance of {openingBalance}")]
public void CreateAnAccount(decimal openingBalance) => _context.Balance = openingBalance;
[StepText("I withdraw {amount}")]
public void Withdraw(decimal amount) => _context.Balance -= amount;
}
public class AccountAssertionsSteps
{
private readonly AccountContext _context;
public AccountAssertionsSteps(AccountContext context) => _context = context;
[StepText("the account balance is {expectedBalance}")]
public async Task TheBalanceIs(decimal expectedBalance) =>
await Assert.That(_context.Balance).IsEqualTo(expectedBalance);
}
TUnit assertions are awaited, so the assertion step returns Task. BDTest accepts synchronous and asynchronous steps in any position.
3. Write the scenario
Inherit from TUnitBDTestBase<TContext>. Expose step classes as properties, not fields, so that each test gets steps bound to its own Context.
using BDTest;
[Story(AsA = "account holder",
IWant = "to withdraw money",
SoThat = "I can use my savings")]
public class WithdrawalTests : TUnitBDTestBase<AccountContext>
{
private AccountSteps Account => new AccountSteps(Context);
private AccountAssertionsSteps AccountAssertions => new AccountAssertionsSteps(Context);
[Test]
[ScenarioText("Withdrawing money reduces the account balance")]
public async Task WithdrawMoney()
{
await Given(() => Account.CreateAnAccount(100m))
.When(() => Account.Withdraw(25m))
.Then(() => AccountAssertions.TheBalanceIs(75m));
}
}
Test methods return Task and await the chain. Call BDTestAsync(cancellationToken) instead when execution must observe a cancellation token.
4. Run it
TUnit test projects are executables that run on Microsoft.Testing.Platform:
dotnet run --project MyApp.AcceptanceTests
BDTest records the scenario, its steps, their status and any console output. If a step throws, the exception is recorded against that step, the remaining steps are marked Not run, and the exception is rethrown to the test runner.
5. Produce a report
Install BDTest.Reporting at the same version as the adapter for this section:
dotnet add package BDTest.Reporting
Add an assembly-level hook that writes an HTML report after all tests have run:
using BDTest;
using BDTest.Helpers;
public class AccountReports
{
[After(HookType.Assembly)]
public static async Task WriteReport()
{
var assembly = typeof(AccountReports).Assembly;
try
{
var run = BDTest.TUnitBDTestAssembly.GetRun(assembly);
var html = BDTestHtmlHelper.GetTestHtmlReport(new BDTestRunDescriptor
{
Environment = "Local",
Tag = "Acceptance tests",
}, run);
await File.WriteAllTextAsync(Path.Combine(AppContext.BaseDirectory, "BDTest-Report.html"), html);
}
finally { BDTest.TUnitBDTestAssembly.Dispose(assembly); }
}
}
Open BDTest-Report.html from the test project's output folder in a browser. To share results with a team, use the report server instead.
Next steps
- Scenario rules: which steps can follow which.
- Step text: control how steps read in reports.
- Scenario outlines: run one scenario over a table of examples.