-
Notifications
You must be signed in to change notification settings - Fork 0
Writing scenarios
The vocabulary of an xWellBehaved.net scenario is very similar to a Cucumber scenario. Their anatomy, however, is rather different. An xWellBehaved.net scenario is contained completely in code.
The basic anatomy of an xWellBehaved.net scenario is:
- A containing assembly
- A containing type (e.g. C# class)
- A scenario (e.g. C# method)
- Some steps (e.g. delegates defined inside a C# method)
A containing assembly contains the tests for the features or specs of your system under test (SUT). For example, if the library you want to test is named Widgets
, you might create a library named Widgets.Features
or Widget.Specs
(see What kind of tests can I write using xWellBehaved.net). This is the equivalent of creating a test library using xUnit.net or NUnit.
A containing type groups your scenarios together. For example, if one of your features is a calculator, you might create a class named CalculatorFeature
. This is the equivalent of creating a test class to contain a set of xUnit.net facts or NUnit tests.
The name of your scenario should describe it adequately. For example, if you want to cover the scenario of adding numbers together with your calculator then you might name the scenario Addition
.
The steps within a scenario contain the code which is executed during the scenario and are typically defined using the vocabulary of Gherkin.
namespace Widgets
{
public class Calculator
{
public int Add(int x, int y) => x + y;
}
}
namespace Widgets.Features
{
using Xunit;
using Xwellbehaved;
// In order to build complicated widgets
// As a widget builder
// I want a calculator for performing basic arithmetic
public class CalculatorFeature
{
[Scenario]
public void Addition(int x, int y, Calculator calculator, int answer)
{
// NOTE: This is a simple example. You provide a simple `Action`
// to the framework, and the framework wires it up for you. All
// the usual language rules apply as method and class complexity
// increase. That is entirely up to you to manage that complexity.
"Given the number 1".x(() => x = 1);
"And the number 2".x(() => y = 2);
"And a calculator".x(() => calculator = new Calculator());
"When I add the numbers together".x(() => answer = calculator.Add(x, y));
"Then the answer is 3".x(() => answer.Should().Be(3));
}
}
}
Let's examine this scenario bit by bit.
// In order to build complicated widgets
// As a widget builder
// I want a calculator for performing basic arithmetic
public class CalculatorFeature { ... }
As in Cucumber, this is free text which is not parsed. Its purpose is to describe the feature to the reader. The name of the feature is the class name, which is the equivalent of the first line of a Cucumber feature introduction.
[Scenario]
public void Addition(int x, int y, Calculator calculator, int answer) { ... }
The [Scenario]
attribute marks the method as a scenario and is the equivalent of the xUnit.net [Fact]
and NUnit [Test]
attributes. The name of the scenario is the method name.
The parameters of the scenario method are simply a convenient way of declaring the variables which will be used within the scenario steps. The method will be invoked with default values passed for each parameter, i.e. null
for reference types and zero values for value types. You can also supply example values for these parameters for executing scenarios in a data-driven/spec by example manner (see Scenarios with examples). If you do not want to declare your variables as parameters in this way, you can declare them within the scenario body instead.
[Scenario]
public void Addition()
{
var x = 0;
var y = 0;
Calculator calculator = null;
var answer = 0;
...
}
Lastly are the steps themselves. In this example, they are directly equivalent to Cucumber steps. Like Cucumber, xWellBehaved.net does not distinguish between each type of step. It is up to you to organize them as you see fit. You do not have to use the Given, When, Then language. The names of the steps themselves are free text and you can choose to use any convention you like (see Step names). If you do not like the x
method you can extend xWellBehaved.net with your own methods (see Extending xWellBehaved.net).
Warning: Complexity is the name of the game, or, rather, managing that complexity. The key to writing good xWellBehaved.net based unit test code is to recognize your step entry point as the launching point for any of your scenario test steps. What do we mean by that?
"This is your launching point, or, rather, the System.Action callback there in".x(() => { ... });
All the usual language rules apply. So we could just as easily write something like the following.
void OnAnotherLaunchingPoint() { ... }
"Another launching point".x(OnAnotherLaunchingPoint);
Accessing code apart from these entry points is dangerous and you will probably sustain access violations or similar rude behavior from the run time environment upon execution.
Side node: This guidance is especially accurate when accessing xUnit.net furnished resources such as ITestOutputHelper
. It is critical that you do so within the context of the xWellBehaved.net entry points.
We are open to receiving feedback that enhances the overall end user experience using our framework, of course. Feel free to also stop by and offer us a handshake on Gitter.
For an explanation of what happens when you run a scenario, see Running scenarios.
- Home
- Quick start
-
Documentation
- Writing scenarios
- Running scenarios
- Package dependencies
- Debugging scenarios
- Assertions
- Step names
- Debugging Scenarios with examples
- Background methods
- TearDown methods
- Async steps
- Object disposal
- Rollback
- Skipping steps and scenarios
- Step metadata
- Continuing on failure
- Step filters
- Changes in xBehave.net version 2.0
- Changes since deriving from xBehave.net
- Extending xWellBehaved.net
- FAQ
- Known Issues
- Contributions