StoryData is a plain old C# object (POCO) of your choice. An instance of this class will be instantiated at the beginning of your test arrangement and is accessible in every story. This means you can enrich your StoryData as your stories progress.
You can only have one instance of StoryData per configured scenario.
The story data can be accessed from within a story from the scenario context.
The example below demonstrates accessing the story data to get the correct id to use in the URL and updating the story data with the result of the API call.
A Chapter
is used to navigate through our Scenario
. It contains Stories. It allows us to guide the test author what they can do next.
The important thing to remember is that the Chapter
can receive the output of the previous Story
as an input via the StoryData
. We do that by inheriting from the base Class Chapter
and specify our StoryData class as the Generic Type. Our class should look like this.
Now we have our Chapter we can add our Stories.
As described in the introduction a StoryBook
is the starting Chapter
of our Scenario
.
Lets illustrate what that really means, with an example. I will use my fictitious Banking API to demonstrate.
We want to test the scenario in our Banking API that the balance of our account is correct after we make a cash deposit.
Before we can do that there are a couple of steps we need to do to test that scenario.
Create the bank account
Deposit the money into the correct account.
In Bard after we have created the bank account we can pass the results from the 'Create Bank Account' story into the 'Deposit Money' story this is useful because we probably need to know the Id of the newly created bank account.
Lets demonstrate what that would look like with a code example.
Lets talk about what's going on in that example. The first thing to note is we are calling the base method When
and this is where we do the 'work' of the story is done by calling our create bank account endpoint. Notice that we are making our API call using our TestContext
on line 10.
Now on line 12 we are returning we are updating our StoryData with the response from the API call, making that data available to the next story.
Finally on line 14 we call the generic ProceedToChapter
base method. This instructs our fluent API what the next Chapter
is that will help us build out our Fluent Test interface.
This section how to perform test arrangement with Bard. This is the part of the test that tells the 'story' of the state of the system for the test to run.
In my opinion this is the most important part of an integration test and probably the part that is most overlooked when writing tests.
When testing a complex domain test arrangement can become convoluted. A framework like Bard encourages the developers to invest more time upfront to building a library of stories. However these stories be reused and composed into different scenarios. Overtime this will make your tests easier to write, easier to understand and easier to maintain.
In summary the goal of Bard is to make your tests:
Easy to read and understand.
Easy to reuse.
More maintainable
Easy to compose new tests.
Bard has the concept of a StoryBook
. The Story Book describes the way you can interact with you API/Domain in order to put it in the required state. Think of a StoryBook
as the opening chapter of your scenario.
A StoryBook
is the entry point into a Scenario
.
A StoryBook
is made up of Chapters
& Stories
.
The StoryBook
is the starting Chapter
of our Scenario
and from there we can select a Story
that take us to the next Chapter
that contains other stories.
Chapters are our decision points. What can we do given what has happened already.
Stories are the building blocks. This is where the work is done and we make changes to our domain, usually through making an API call.
What makes Bard different from other BDD style testing frameworks is that the output from one Story is the input to the next chapter and the containing stories.
Bard uses a functional approach to test arrangement and employs the Collection Pipeline Pattern
Collection pipelines are a programming pattern where you organize some computation as a sequence of operations which compose by taking a collection as output of one operation and feeding it into the next. - Martin Fowler
This is a powerful feature which means the output of one Story can be the input to the next Story. This can make your tests easier to write and easier to understand.
As described in the the story is where the work is actually done where we can perform our test arrangement.
We can have a few different flavors of stories depending upon where we are in the scenario and how we want to use the story.
A starting story is a story that is written in the opening chapter of a StoryBook
. It is different from other stories in the fact that it does not have any input from other stories.
A simple story has come from another chapter and therefore has input from the previous story.
Notice we have access to the context and the bankAccount on line 3.
Sometimes in the course of a scenario that it makes sense that we want to reuse our story in another chapter. For example with our Banking example after we create a bank account we can either make a withdrawal or a deposit. If we make a withdrawal we should be able to make a further withdrawal or a further deposit. In essence we want to be able to recursively make withdrawals and deposits using our fluent interface.
We could just copy and paste the code into each chapter but that could present a maintenance problem in the future.
However because Bard takes a functional approach to describing it's stories we can define a common library of Story functions and compose our Chapter with those functions.
Lets demonstrate with an example:
What does this mean?
ScenarioContext this is our test context.
DepositRequest this is the API parameter that is passed into the function which means we can pass it from our story rather than hard code it in this function.
Now in our Chapter we write our Story by referencing our Function
Notice that on line 4 we are calling the base Method Given
and passing in a function that describes how to create our API request object.