Writing scenarios - adamralph/xbehave.net GitHub Wiki
The vocabulary of an xBehave.net scenario is very similar to a Cucumber scenario. Their anatomy, however, is rather different. An xBehave.net scenario is contained completely in code.
The basic anatomy of an xBehave.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). E.g. 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 xBehave.net?). This is the equivalent of creating a test library using xUnit.net or NUnit.
A containing type groups your scenarios together. E.g. 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. E.g. 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.
Example
namespace Widgets
{
public class Calculator
{
public int Add(int x, int y) => x + y;
}
}
namespace Widgets.Features
{
using FluentAssertions;
using Xbehave;
// 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 method should only contain step definitions.
// It doesn't make sense to write any code outside them.
// (See the note below under "Steps".)
"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.
Feature Introduction
// 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 Name
[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.
Parameters
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 don't want to declare your variables as parameters in this way, you can declare them within the scenario body instead. E.g.
[Scenario]
public void Addition()
{
var x = 0;
var y = 0;
Calculator calculator = null;
var answer = 0;
...
Steps
Lastly are the steps themselves. In this example, they are directly equivalent to Cucumber steps. Like Cucumber, xBehave.net does not distinguish between each type of step. It is up to you to organise them as you see fit. You don't 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 don't like the x
method you can extend xBehave.net with your own methods (see Extending xBehave.net).
Note: You should not be writing any code inside a scenario method which is outside one of the step definitions. It doesn't make sense to do so, since a scenario method only exists in order to define the steps, and it is executed in a context which makes that assumption. Some things, e.g. an ITestOutputHelper
will not work when called outside a step. Indeed, it should never be necessary to write code outside of a step. If you ever find a need to do so, please let us know by either raising an issue or chatting to us. There are exceptions to every rule, and it would be extremely valuable for us to know about them.
For an explanation of what happens when you run a scenario, see Running scenarios.