Quantcast
Channel: BDD framework for NET
Viewing all 197 articles
Browse latest View live

Gherkin Conventions for Readable Specifications

$
0
0

This article was written by Sophie Keiblinger. Sophie has been a software test engineer at TechTalk since 2014 and is based in Vienna, Austria. She is passionate about BDD and test automation because it “encourages and fosters communication between all stakeholders and leaves more time for exploring and finding the interesting stuff”.

Developers have coding guidelines and formatting tools that help them keep their code clean, maintainable and readable as well as increase recognisability. You might think that Gherkin has no need for such conventions, given that Gherkin is essentially written using a natural language. But giving your brain easily recognizable and expected patterns to work with makes reading feature files and working with Gherkin much faster and easier.

We have been asked if there are general conventions for Gherkin that should be followed. While there are none that we are aware of, we can share the guidelines that we generally agree on in our projects at TechTalk.

Discernible Given-When-Then Blocks

In theory, your scenarios can be as simple as a single Given, When and Then step each. However, in real life they tend to grow and have multiple steps for each of these keyword. In order to quickly spot where one block ends and another one begins, you can indent the steps starting with “And”. Then your scenario would look something like this:

Now, the steps in this example don’t use tables. If you use a lot of tables, you might find the visible pattern unruly or uneasy for the eye:

An alternative is to have every step start with the same indentation, and add an extra newline before the next keyword block. However, this makes your scenario longer, which is something you generally want to avoid – you don’t want to have to scroll in order to see all the information for a scenario. Plus, I personally prefer to use extra newlines differently (but more on that later).

In the end, it is probably best to decide with your team what is more important to you and which version you prefer.

Steps with Tables

We often use tables in our steps. In order to make it immediately recognizable that a step needs further input from a table, we use a colon at the end of the step. This helps when using Intellisense, which does not include table previews but will display the colon:

You can also formulate your step to make this clearer:

Notice that I used “following” in my step’s wording. I can only recommend this approach as well. However, in most of my projects, I can be sure that a colon at the end of the step means it is followed by a table. Indenting the table also makes it clear that it is part of that particular step.

Reducing Noise

In order to reduce noise, we recommend using default values for fields that the system requires, but that are not relevant to your scenario. For example, if you want to test the validation of a date of birth, you don’t need to know the person’s name, academic title or social security number. These might be mandatory fields in your application, but have no bearing on the outcome of your scenario.

This also works with steps that have tables, in which case you only include the columns needed for your scenario.

Parameters in Steps

As you can see in the example above, I used single quotation marks ‘like this‘ around step parameters. This makes it easy to spot parameters in a step, even with just very simple syntax highlighting or none at all.

Newlines within Scenarios

Using newlines helps your brain group the right information together and makes it easier to tell where the next logical unit starts. While the text may still be readable without newlines between steps/blocks when they are short, it becomes very difficult to read once tables are involved.

If your scenario starts getting long, newlines before each block might help to make it more readable. In the case of steps containing tables, I would always add newlines between each step:

I would also recommend adding a newline before your examples block:

Newlines between scenarios and separator comments

The more scenarios you have in the same file and the bigger they are, the harder it becomes to find the point where one scenario ends and another one starts. As a visual aid, we add 2 newlines between scenarios. Usually we also add a comment separator:

If you place the separator comment directly above your scenario (or its tag), they will be displayed when all scenarios are collapsed (including 2 newlines):

If you instead add a newline between the separator and scenario (or it’s tag), then you will see the scenarios neatly stacked after each other when they are collapsed (with 1 newline separating them):

All of these formatting conventions are ones that we generally agree upon in our projects. But remember that they are all just suggestions and approaches that we have developed over time to deal with various issues we have faced. It’s important to figure out what works best for you based on our own set of challenges and needs. So rather than seeing these formatting conventions as set in stone, try to use them to inspire you to find ways to make your own feature files more readable and easier to work with. And feel free to share any tips you may have!

I am sure that no matter what conventions you agree upon with your team, they will make working with and maintaining your Gherkin specifications easier.

All of the examples above can also be found on GitHub here.

The post Gherkin Conventions for Readable Specifications appeared first on SpecFlow - Cucumber for .NET.


SpecFlow & SpecFlow+Runner 3.1 Are Here!

$
0
0

We are happy to announce the release of SpecFlow and SpecFlow+Runner 3.1. This update fixes a lot of smaller issues found by our community members over the last months, but we have also added some bigger features to this release.

What’s new?

1. .NET Core 3.0 Support

With the addition of nullable reference types in C# 8, we needed to make some changes to our binding execution code. These changes have now been implemented, and all our tests for .NET Core 3.0 are successful.

2. Cucumber Messages

The integration of Cucumber messages is a joint effort with the Cucumber community to create a unified protocol for exchanging data between different components of SpecFlow and Cucumber. There will be no longer be a need to implement separate HTML report generators for each Cucumber implementation and SpecFlow. Cucumber messages allow you to use the same HTML report generators for SpecFlow and Cucumber.

SpecFlow 3.1 contains the first set of Cucumber Messages: TestRunStarted, TestCaseStarted, TestCaseFinished and TestRunFinished. We plan on implementing additional messages that allow you to generate different output formats/reports in future releases.

Cucumber messages are currently disabled by default. We plan to enable them by default with SpecFlow 3.2. You can find the current documentation here.

Changes Related to Cucumber Messages

For end users, the introduction of these messages does not bring any changes from previous versions, as we have not changed SpecFlow’s overall behaviour.

However, we changed the internal workings of how scenarios are ignored. In the past, we generated the appropriate ignore attribute for the test runner in the code-behind file. But as we now also need to send a cucumber message for skipped scenarios, we no longer generate the ignore attribute and skip the scenario at runtime instead.

3. Docker Support in SpecFlow+Runner

One thing we learned is that just because SpecFlow is compiled and runs with .NET Core, that does not mean it will run on Windows, Linux and Mac OS. Not all aspects of.NET Core are applicable to all 3 operating systems. SpecFlow+Runner 3.1 now uses APIs that also work on Linux and MacOS. All SpecFlow+Runner features should also now work in Docker. If not, please open an issue.

If you want to generate SpecFlow+ Runner reports on non-Windows operating systems, you need to install mono. Details can be found here.

A sample SpecFlow+ Runner project demonstrating support for Docker support can be found here.

4. Gherkin 6 “Rule” Keyword

SpecFlow 3.1 supports the new Gherkin 6 Rule keyword.

However, the Visual Studio Extension does not yet support this keyword. Using it will stop the syntax highlighting from working.

5. Project Template

To make it easier to create new SpecFlow projects, we have created a project template that you can access with dotnet new. You can find more information on installing the template and using it to create new projects here.

Thanks to all our amazing contributors!

Thanks to the following users for their contributions to SpecFlow 3.1:

Detailed Changelogs

Changelog for SpecFlow

Features:

  • Support for .NET Core 3.0
  • Cucumber messages:
    • First set of cucumber messages are implemented:
      • TestRunStarted
      • TestCaseStarted
      • TestCaseFinished
      • TestRunFinished
    • Documentation: https://specflow.org/documentation/Cucumber-Messages/
    • Cucumber messages can be enabled via specflow.json and app.config
    • File sinks for Cucumber messages can be configured
  • Support for Gherkin v6 “Rule” keyword
  • Support for discovering step definitions from attributes that are derived from Given, When, Then or StepDefinition attributes (related to Issue 1745)

Fixes:

  • AfterTestRun hook can take longer than 100ms on .NET Core (Issue 1348)
  • Adjust parameter names generation in scenario outlines (Issue 1694)
  • specflow.json can be used in SpecFlow+Runner with Process test thread isolation (Issue 1761)
  • specflow.json is copied to output directory automatically (Issue 315)
  • Step definition method with “object” parameter type might not match

Changelog for SpecFlow+Runner

Features:

  • Support for .NET Core 3.0
  • Support for SpecFlow 3.1

Fixes:

  • Test execution in Docker works
  • Improved performance when executing projects with a lot of scenarios (> 10,000)

Changes:

  • Changed the communication between main process and child processes from named pipes to TCP/IP

The post SpecFlow & SpecFlow+Runner 3.1 Are Here! appeared first on SpecFlow - Cucumber for .NET.

SpecFlow Project Template with dotnet new

$
0
0

To make it easier to create new SpecFlow projects, we have created a project template that you can access with dotnet new. As with all templates, you need to install them first before you can use the templates. More information on installing templates can be found here.

To install the template, execute the following command from the command line of your choice:

dotnet new -i SpecFlow.Templates.DotNet

Once the installation is complete, you can create new project with the following command:

dotnet new specflowproject

This creates a .NET Core 3 project with SpecFlow+Runner set as default test runner. You can define a different test runner or target framework using the following optional parameters.

  • framework: the following values are supported:
    • netcoreapp3.0 (default): .NET Core 3
    • netcoreapp2.2 : .NET Core 2.2
    • net472: .NET 4.7.2
  • unittestprovider: can be one of the following:
    • specflowplusrunner (default): SpecFlow+ Runner
    • xunit: XUnit
    • nunit: NUnit
    • mstest: MSTest

Example:

dotnet new specflowproject --unittestprovider xunit --framework netcoreapp2.2

This creates a new project with XUnit as the unit test provider, and targetting .NET Core 2.2. The project is created with a pre-defined structure that follows best practices. The project includes a single feature file (in the Features folder) and its associated steps (in the Steps folder).

Item templates

In addition to the project template, we added also some item templates to the template pack, which includes the following:

  • specflow-feature: .feature file in English
  • specflow-json: specflow.json configuration file
  • specflow-plus-profile: Default.srProfile (SpecFlow+Runner configuration)

If you have additional ideas for the templates, please open a GitHub issue here.

Big thanks go out to our intern Manuel for creating these templates!

The post SpecFlow Project Template with dotnet new appeared first on SpecFlow - Cucumber for .NET.

ScenarioContext and FeatureContext in SpecFlow 3.0 and Later

$
0
0

With SpecFlow 3.0, we marked ScenarioContext.Current and FeatureContext.Current as obsolete, to make clear that you that you should avoid using these properties in future. The reason for moving away from these properties is that they do not work when running scenarios in parallel.

So how do you now access ScenarioContext and FeatureContext?

Let’s first focus on ScenarioContext. Previously you would access ScenarioContext using code similar to this:

[Binding]
public class Bindings
{
    [Given(@"I have entered (.*) into the calculator")]
    public void GivenIHaveEnteredIntoTheCalculator(int number)
    {
        ScenarioContext.Current["Number1"] = number;
    }

    [BeforeScenario()]
    public void BeforeScenario()
    {
        Console.WriteLine("Starting " + ScenarioContext.Current.ScenarioInfo.Title);
    }
}

As of SpecFlow 3.0, you now need to use context injection to acquire an instance of ScenarioContext by requesting it via the constructor.

public class Bindings
{
    private readonly ScenarioContext _scenarioContext;

    public Bindings(ScenarioContext scenarioContext)
    {
        _scenarioContext = scenarioContext;
    }
}

Once you have acquired the instance of ScenarioContext, you can use it with the same methods and properties as before.

So our example will now look like this:

public class Bindings
{
    private readonly ScenarioContext _scenarioContext;

    public Bindings(ScenarioContext scenarioContext)
    {
        _scenarioContext = scenarioContext;
    }

    [Given(@"I have entered (.*) into the calculator")]
    public void GivenIHaveEnteredIntoTheCalculator(int number)
    {
        _scenarioContext["Number1"] = number;
    }

    [BeforeScenario()]
    public void BeforeScenario()
    {
        Console.WriteLine("Starting " + _scenarioContext.ScenarioInfo.Title);
    }
}

But what about FeatureContext?

In normal bindings as well as Before/After Scenario, Before/After ScenarioBlock and Before/After Step hooks, you can acquire the FeatureContext in the same way as ScenarioContext. However, if you want to use it together with Before/After Feature hooks, you need to acquire it via a function parameter.

Previously:

public class Hooks
{
    [AfterFeature()]
    public static void AfterFeature()
    {
        Console.WriteLine("Finished " + FeatureContext.Current.FeatureInfo.Title);
    }
}

Now:

public class Hooks
{
    [AfterFeature]
    public static void AfterFeature(FeatureContext featureContext)
    {
        Console.WriteLine("Finished " + featureContext.FeatureInfo.Title);
    }
}

Conclusion

The number of steps required to deal with these obsolete warnings is minimal, unlike some cases, where a lot of refactoring is needed. You can find working examples in our example collection, one example from before refactoring and one after refactoring.

We have also made a short video to demonstrate the process and show how little there is to it:

ADD VIDEO LINK

The post ScenarioContext and FeatureContext in SpecFlow 3.0 and Later appeared first on SpecFlow - Cucumber for .NET.

SpecFlow Support over the Holiday Period 2019-2020

$
0
0

Merry Xmas!

The SpecFlow team will not be reachable over the holiday period, from 23 December 2019 to 6 January 2020. During this time, we will not be able to reply to any issues reported on GitHub or to mails sent to SpecFlow+ support. Support will resume on 7 January 2020.

We hope that you also enjoy the holiday period, and if you are unlucky enough to be in the office during this period, we thank you for your understanding.

If you need help during this time period, we suggest reaching out to the SpecFlow or SpecFlow+ Google Groups, where you can receive help from other members of the community. We will check on any issues that are reported on GitHub or to SpecFlow+ support once we have returned to the office.

The post SpecFlow Support over the Holiday Period 2019-2020 appeared first on SpecFlow - Cucumber for .NET.

Given-When-Then With Style

$
0
0

I’m very excited to partner with Specflow on a new series of weekly blog articles, aimed at helping you get the most out of Given-When-Then feature files.

Tips and tricks for better feature specifications

Earlier this year, before this damned virus situation, I published the results of a research aimed to understand what’s changed in the Specification by Example space in the ten years since the book came out. One of the most surprising discoveries was how popular the Given-When-Then format became. When I wrote the book, it was used by a tiny minority.

Now, more than 70% of the teams that drive specifications and tests with examples do so using Given-When-Then steps. No doubt, one of the key reasons is that the format is very easy to get started with, but unfortunately, lots of people never move beyond the basics. That’s what we’re trying to fix with “Given-When-Then With Style” challenge.

The goal of this series of articles is to help you build more successful software through better-shared understanding, powered by examples.

  • For those new to specification by example, we’ll explore how to get started easily and how to avoid the most common problems.
  • For readers that are more experienced, we’ll cover how to capture examples through collaborative analysis, how to structure and organize feature files when dealing with complex domains, and how to ensure that documents stay easy to understand and easy to maintain over a long period.
  • For the expert readers, we’ll cover tips and tricks on collaborative modeling, and how specification by example relates to other techniques.

Although the articles will be published on the SpecFlow website, they are tool-agnostic. You’ll be able to use most of the ideas with alternative tools, such as Cucumber, but also as more general techniques when doing collaborative analysis, even if you never automate any tests.

Each week, we’ll post a challenge on this web site, explaining a common problem people face when trying to capture examples with Given-When-Then steps. We invite the community to participate. Send your ideas and solutions, and the following week we’ll publish an analysis with answers.

List of published challenges

In case you have missed a challenge, here is an overview of our past challenges:

Do you have any problem you’d like us to explore in one of the future posts? Let us know!

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the challenge with your friends and team members by clicking on one of the social icons below 👇

The post Given-When-Then With Style appeared first on SpecFlow - Cucumber for .NET.

How to set up something that’s not supposed to exist? #GivenWhenThenWithStyle

$
0
0

This week’s challenge is the tricky problem of explaining a missing value.

What’s the best way to describe a Given for a value that’s not supposed to be there? For example, starting the specification from a point of a user who does not yet exist in a database, or a product that’s not been set up. “Given a user that doesn’t exist” sounds vague, but “Given user John that does not exist” sounds silly – how can we talk about John if he doesn’t exist yet?

Key examples should ideally be specific enough to avoid ambiguity, but it’s difficult to be specific about something that does not exist. Such problematic Given-When-Then scenarios are usually overly generic, and do not really help with shared understanding or testing. Here’s a typical trivial example that should ensure unique accounts:

Given user John does not exist
When John tries to register
Then the registration is successful

Given user John exists
When John tries to register
Then the registration fails

This set of scenarios seems simple but it can hide many important constraints, and provide a false sense of shared understanding. A team might think they nailed down a feature, but develop something full of bugs. Scenarios such as these leave too much for later exploratory testing, overloading the team to discover seemingly unexpected problems that could have easily been predicted and documented with better examples.

As a trivial counter-example, consider what should happen if John tries to register with an email already assigned to a different user. For a more complex edge case, consider what should happen if two Johns tried to register at the same time. And what is “John” anyway in this case? Is it a personal name or a username? If it’s a personal name, should we really prevent two people with the same name from opening two different accounts?

Oversimplified examples often lead to overcomplicated test automation. Proving that a user successfully registered usually requires accessing a database, which means that the related tests will be slow and brittle. Accessing external systems is a huge performance penalty compared to in-process tests. Asynchronous networking introduces a whole set of technical edge cases that have nothing to do with user registration business rules.

Data persistence leads to issues around reproducibility. John might not exist in the database the first time you run a test, but it surely will be there after that test completes. To make such a test repeatable, you’ll either need to introduce complex test set-up or clean-up procedures or make the examples even more generic by introducing placeholders for random or unique values.

Database resources are usually difficult to set up from scratch, so team members and test automation systems sometimes share testing databases, which can lead to issues around reproducibility and isolation. If two people execute tests against the same database at the same time, one might add data that the other does not expect, causing tests to fail intermittently without a good explanation.

Read-on for a selection of good ideas we’ve received and our suggestions on how to handle scenarios such as this one.

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the challenge with your friends and team members by clicking on one of the social icons below 👇

The post How to set up something that’s not supposed to exist? #GivenWhenThenWithStyle appeared first on SpecFlow - Cucumber for .NET.

The State of Open Source Testing – key findings on BDD

$
0
0

We love Open Source Software. At SpecFlow we believe in creating great software through collaboration. We appreciate any creative input during our development journey.

Open Source does just that, simply on a larger scale, which is why we have been running our very own .NET BDD project Open Source from its inception on. Once the idea was born to ask a greater community about their Open Source preferences and experiences, it was a no-brainer for us to join our peer sponsors Tricentis, Flood, and TestProject in conducting this year’s “The State of Open Source Testing” survey.

Let’s start with the demographics. Together we were able to motivate over 1,800 people to participate. Most of our participants (66%) are working currently in a QA related role. The majority (61%) of our respondents lives in Asia and their preferred programming language is Java (67%). Lastly and most importantly 30% are heavy BDD users. With these demographics in mind we need to remind ourselves that BDD is a team activity and we are looking mostly through the tester’s eyes with our findings.

Here are our 5 key findings on Behavior Driven Development (BDD) and Open Source.

1) Writing good Gherkins requires practice

A sizable number of participants indicated to us that one of their main challenges in adopting BDD widely was to explain to their entire team how to write well written Gherkin examples. Users struggles are manifold, but the 2 main areas turned out to be the abstraction that comes with it and to write specific enough while keeping it simple. Once the practice was established communication amongst the team was much faster.

2) Living Documentation is a hidden gem

One of the most impactful benefits of BDD is to generate so-called Living Documentation from the Gherkin scenarios generated and used for automation. The Idea of Living Documentation is to always keep the specification of your software current – a historic pain point in software change management. Specification usually is current at initial release and then dilutes over time as software version numbers increase. The survey revealed that less than half (43%) of BDD users, that are either specifying their scenarios in Gherkin and/or are automating their scenarios, are using them to document the behavior of their application.

3) SpecFlow and Cucumber are the tools of choice

BDD has a growing community and ecosystem. The two Open Source projects spearheading the evangelization of the BDD market with an astonishing collective 94% are Cucumber (Java, JS, Ruby, …) and SpecFlow (C# .Net). Teams embracing BDD seem to align their preference with their choice of programming language and choose predominantly one of the two projects.

4) BDD is used in pieces

Even though BDD is a methodology that runs through the entire development cycle from Design to Release and wants to involve all 3 agile amigos (QA, DEV, BIZ) users seem to be choosing segments of its ideas while only exactly 50% of responses indicate complete use of all concepts of BDD. 51% are using it mostly for specification of behavioral examples in Given-When-Then format. Almost half (46%) use it predominantly for automation – which underlines the importance of acceptance test automation in today’s software development.

5) BDD projects run with 50% higher efficiency

The common consensus amongst the participants was that their projects applying BDD run substantially more efficient. On average 49%. The spread within these results was (not surprisingly) significant with users claiming anywhere from 0-100%. While investigating into the data we found a strong correlation between BDD proficiency level and (Proficient and Expert) and the efficiency claimed. With 76% of users being proficient their average was at 68%.

Our Conclusion

BDD has a growing community with lots of enthusiasm behind it. The community is clearly maturing and realizing strong benefits from BDD already. Cucumber and SpecFlow are nurturing the community actively and have strong followership. There are still some benefits that BDD teams are yet to enjoy and need to grow into using (e.g. Living Documentation). Agile teams need to embrace BDD together while Gherkin craftsman need to continue to practice their BDD authoring skills.

If you are interested in additional detail or would like to read more about the survey, check out the links below and follow us on social media (twitter, linkedin) to hear about our plans for the next survey in 2021.

The post The State of Open Source Testing – key findings on BDD appeared first on SpecFlow - Cucumber for .NET.


Solving “How to set up something that’s not supposed to exist?” #GivenWhenThenWithStyle

$
0
0

Last week, we started a community challenge to suggest improvements to a badly worded Given-When-Then feature file, specifically addressing the problem of setting up something that’s not supposed to exist. This article contains an analysis of the responses, some tips on a more general approach to solving similar problems, and a new challenge for this week.

I’ve outlined some of the most common problems with describing something that doesn’t exist in the original challenge, so I will not repeat them here – check the linked post for a refresher.

We received lots of excellent responses to the first challenge, from people in sixteen different countries. Alister Scott even wrote a wonderful blog post about his proposal.

Describe what exists instead

Although the problems with describing something that doesn’t exist sound like technical challenges that are purely related to testing, they are often just symptoms of an unexplored model. It’s possible to solve or avoid most of them by exploring the model differently, and ensuring shared understanding between team members and stakeholders. Concrete examples are a great way to do that, and many solutions tried to make things more concrete by introducing different properties of users, or talking about users before they register in a different way.

For example, several suggestions involved introducing a specific qualifier, such as an “unregistered user”. This helps to differentiate between two different types of entities: one that exists before registration, and another that exists after. Similarly, some solutions tried to move away from an ambiguous user identifiers, such as “john”, to more concrete data available even users aren’t registered. For example, it’s possible to consider user emails even before they sign up to our system. The following suggestion, which also came in anonymously, illustrates this nicely:

Given user john@doe.com tries to create an account
When there is already an account for john@doe.com 
Then registration is not possible

All these attempts are going in the right direction, but they are still just workarounds for a problem and they aren’t solving it fully. On a conceptual level, a great way to approach similar problems is another anonymous suggestion, “Describe the state of the system without the value”. Instead of trying to describe something that is not supposed to exist, we can describe what else exists instead.

There are two ways of approaching this suggestion practically. The first, suggested by Mathieu Roseboom, is to look outside the system. Mathieu wrote “I’d emphasize that John is a person, and not a user.” Rather than talking about unregistered users that do not exist, let’s talk about people that do exist. The suggestions that tried to use emails to describe unregistered users lean in that direction, without fully benefiting from it. Thinking about person that exists outside of our system might leads us to discover about some other attributes, which could be important for the current feature.

The second way to specify something that exists instead of talking about vague non-existing entities is to describe all the other registered users. For example, one response suggested splitting the problem into two cases:

Given list of users is empty
...
Given a "normal" database

Another reader suggested setting up a separate feature file, that just creates the relevant users, and then technically ensuring that all other feature file tests run after it.

Feature: Setup users

Scenario: Register users
The following users are registered:
| name | email |
| Rick | rick@mail.com |

David Wardlaw suggested starting the scenario with:

Given the following user is already registered
| First Name | Last Name | Email Address | 
| John       | Smith     | js@bob.com    |
And a new user wants to register with a first name of <First Name>

These three ideas illustrate different levels of visibility that feature files could offer. In the first option, readers will need to know what the “normal” database contains, but the solution is quite easy to reuse across different scenarios. On the other hand, if someone modifies the “normal” database for some unrelated reason, tests might start to break unexpectedly.

The second solution optimises performance because it inserts records only once, and provides better visibility to readers about the assumed state. The downside of this idea is that it imposes a very specific order for executing tests. This feature file must run before everything else, otherwise things start weirdly misbehaving. I usually try to avoid imposing a specific order of test executions, since then we can’t just run a single test when needed. This approach also suffers from implied shared state. If someone updates that central setup file for some unrelated test case, our tests may magically start to fail.

The third suggestion provides full visibility to a reader, and it does not impose any shared state that could cause problems later when things change. This approach might get a bit wordy if we need to set up a lot of users, but there are ways around that as well.

The key problem the first and second solution are trying to solve is the complexity and performance of working with a real database. Databases are slow and difficult to control compared to simple in-memory objects, so tests involving a real database often have to compensate for those downsides somehow. Between the three ideas, I would go with the third one unless there is some very specific performance constraint we want to solve. I promise to come back to this next week, but since we’re not solving database performance in this challenge, let’s just postpone that discussion.

So which approach should we choose? For that, I’d like to explore this problem on a more conceptual level, and explain several techniques which you can use with other similar situations.

Ask an extreme question

In order to reason about the existence of an item (such as a user or a product), we first need to answer a key probing question: where? Where should that item exist or not exist? Defining existence is, at core, reasoning about the state of system data. Figuring where the data resides is critical for three reasons:

  1. To resolve the chicken-and-egg issue of describing something before it exists, since we can talk about an item that simultaneously exists in one context, but not in another.
  2. To clarify the underlying business model, exposing previously hidden domain concepts. This will help us get a better shared understanding about their constraints.
  3. To create fast, reliable and resilient test automation, since identifying new domain concepts makes it possible to introduce additional control points. That’s how we can avoid premature performance optimisation, and having to deal with a real database.

Team members closer to implementation work, such as developers or testers, often think about entities as system data. They will intuitively understand that an entity always exists in a specific location, but they might not be able to share their assumptions about this easily with other team members. People not used to working with system state, such as business representatives, might struggle with that idea. They tend to think about existence in a more absolute way. A helpful probing technique to start the conversation about this concept is to ask an extreme question.

  • What does it mean for a user not to exist? Were they not born yet?
  • What does it mean for a product not to exist? Was it not manufactured? Was it not even designed yet?

These questions might sounds silly, but try them out and you’ll quickly see their value as conversation starters. Extreme questions often result with a hard “No”, and get people to start thinking about multiple contexts, a timeline or a scale. You will start identifying where an entity exists directly before it appears in your context, and what related information you can actually reason about at that point. Mathieu Roseboom’s suggestion to talk about a “person” is one potential outcome of such discussions.

The first set of answers to an extreme question is usually an overly generic statement. For example, the user might exist as a living person, but they don’t yet exist in “the system” or in “the database”. That’s a good starting point for the discussion, since we can now consider what’s known about an entity in different contexts. We can explore the domain much more easily.

Provide a meaningful domain name

A non-registered user likely exists as a living person, so we can talk about their personal name. They do not yet have a username in our system, so we can’t talk about it yet. They likely have an email, which is why so many responses to the challenge focused on that attribute. They might also have a preferred username in mind, which has nothing to do with them existing or not existing in our system. The When part can then become clearer:

When John Michaels attempts to register with the preferred username "john"

A good technique to continue the discussion about something generic such as “system” or “database” is to provide a meaningful domain name. In the Domain-Driven-Design terminology, domain representations of entity stores are called Repositories. When developers hear the word “repository”, they often think about a common technical pattern that involves specific technical operations. Ignore that for the moment – that’s implementation detail. A repository is a meaningful first-order domain concept that encapsulates storing and retrieving data. Tell the rest of the team to imagine it as a box where you keep the users. If they can’t get over that, then think of a different name. I’ll keep using “repository” in this post.

Instead of a user existing or not existing in an absolute sense, we can potentially talk about the repository:

Given John does not exist in the user repository

Even better, let’s make the user repository the subject of the sentence. The repository exists regardless of the users:

Given a user repository that does not include a user called John

The real precondition here is that a user repository exists, with some specific constraints about its contents. There’s no more chicken-and-egg issue. We identified an important first-order domain concept that we can reason about, regardless of a specific user.

Instead of just saying “does not include a user”, which is still a bit vague, we can now start capturing the constraints of the user repository in a more specific way, using an approach very similar to what David Wardlaw suggested. Here’s how I’d start writing it:

Given a user repository with the following users
| username | personal name |
| mike     | Mike Smith    |
| steve123 | Steve James   |
When John Michaels attempts to register with the username "john"
Then the user repository should contain the following users:
| username | personal name |
| mike     | Mike Smith    |
| steve123 | Steve James   |
| john     | John Michaels |

Add counter-examples

We now have a simple but concrete example. We’re not done yet, this is still just a good conversation starter. A good technique to continue the discussion is Simple-Counter-Key:

  1. Add a few more simple examples to show a range of potential outcomes, and discuss them.
  2. Try to provide counterexamples that disprove some of the proposed rules, and could lead to a different outcomes. This often helps to identify additional attributes, and a different structure for the Given section of the scenario.
  3. After you have a good structure, start listing important boundaries that illustrate the key examples.

The first step is to add at least one simple example, which could lead to a different outcome:

When Steve James attempts to register with the username "steve123"

The outcome in this case might be obvious to everyone, but vary the data a bit. What happens if another “Steve James” attempts to register with the username “steveo”?

This can lead to an interesting conversation around the meaning of “uniqueness” and “existence”. Is the purpose of this feature to prevent the same person from creating multiple accounts in the system? If so, we should probably stop Steve from registering again, even with a different username. But if two different people called Steve James try to register, we should not prevent them. We need some other way of determining uniqueness, and personal names are obviously not good enough. Still, we might care about personal names in this scenario to ensure that we’re capturing them correctly in the repository. Are there any other attributes of a person that we should care about? This brings us back to the emails, suggested by several readers responding to the challenge. But how do we know that emails are the right thing to capture?

We can now start discussing the meaning of “unique” as relating to the person, and if emails are the right way to approach it. Some systems need to be lax about this, so they might let people register even with the same email. For example, a multi-tenant cloud app might want to allow opening sub-accounts with the same admin email to provide centralised billing. Those users will genuinely be different people, sharing a common email inbox. Many online systems today want to prevent abuse by validating emails, so they insist that every user has a unique email. Some systems need to be more strict, and they might enforce a unique mobile phone number. For government or banking purposes, even that’s not enough, and we might want to enforce unique date of birth, social security number, passport number or some other government-issued identifier. Different contexts will have different rules around this. Exposing those rules often leads to much better shared understanding, and also points to the attributes which should be captured for the key examples.

Discussing simple examples and counterexamples allows us to probe further into the model. What does it actually mean for a user to exist in our context? Does that mean the user repository has a record matching the specified email, or proposed username, or something else? How do we know that two users are not the same person? To keep things simple, let’s limit our case to just checking that the emails are different. We’ll say that a user exists in the system even if the proposed username is not taken, but we have a different username matching the same email. The simple scenario now evolves to capture the email as well:

Scenario: Users with a unique email should register successfully 

Given a user repository with the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
| steve123 | Steve James   | steve@yahoo.com  |
When John Michaels attempts to register with the username "john" and email "johnm@gmail.com"
Then the user repository should contain the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
| steve123 | Steve James   | steve@yahoo.com  |
| john     | John Michaels | johnm@gmail.com  |

We could add three or four more examples checking for duplicated emails and usernames in the same way, but this will very quickly become difficult to read.

If you’ve not yet read Alister Scott’s blog post about this challenge, now would be a good time to do it. He argues against misusing Given-When-Then to capture similar examples with small variances over and over again, suggesting that tables would be a better match for this case.

Tables are indeed a much better way of capturing related examples, but they can sometimes make it difficult to understand the purpose of a specific example from the group. Alister solves this nicely by pointing out differences between examples in an additional field, called Result. This field contains the explanation why a case failed, and what should have happened instead. For example, one of the results in his blog post is “Registration Unsuccessful – Reset password page displayed with email prefilled”. This points to another interesting thing we might want to check. Knowing that registration failed may not be enough – we need to know that it failed for the right reasons.

Extract scenario outlines

I agree with Alister that misusing Given-When-Then to copy and paste examples is a waste of time (and screen space). Tables are a good solution, and most Given-When-Then tools support listing tables of examples with scenario outlines. Scenario outlines use placeholders marked with <> inside a Given-When-Then scenario, followed by a table of examples with placeholder values. Here is how we could show two failure examples in the same structure:

Scenario Outline: Users with existing emails or usernames should be rejected

Given a user repository with the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
| steve123 | Steve James   | steve@yahoo.com  |
When the Tony James tries to register with <requested username> and <email>
Then the registration should fail with <registration message> 
and user repository should contain only the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
| steve123 | Steve James   | steve@yahoo.com  |

Examples:

| requested username | email             | registration message      | 
| steve123           | steve2@yahoo.com  | Username taken            |
| steve456           | steve@yahoo.com   | Email already registered  |

Note that the When and Then statements contain placeholders relating to the table of examples, which is shown at the bottom.

Evolving Alister’s “result” idea, the registration message helps to explain what’s going on, but it’s also an important domain concept. Now that we identified it, we can talk about what that should contain. For a closely controlled system, we might want to immediately remind an existing user about his username, so he can sign in easily instead of registering or calling support staff. For an open public system, we might want to go in the other direction and mask the fact that an email is registered, and prevent potential malicious actors from inspecting accounts by trying to sign up.

Showing a comment in the table of examples is quite a common trick to explain the purpose of individual examples. Some people prefer to use the first column for that. I tend to use the first column if it’s a generic comment or name of the test, and does not play any role in the testable outputs. If we actually want to check it against some system output (in this case the registration message), I prefer to list inputs on the left and outputs on the right side of the table.

Probe for boundaries

The technique I mentioned earlier is Simple-Counter-Boundary. We still need to do the third step. So far we identified a few simple examples and some counter-examples. That’s a great start because it gives us the structure for discovering boundaries. Inspect each individual property in the Given section and probe with further examples.

What if someone tries to register with the proposed username “Steve123”? How about the email “Steve@Yahoo.com”? An email written in a different case is still the same, so the registration should probably fail. Some systems (notably Amazon AWS Cognito) by default do not enforce case-sensitive username checks, so two people could register with just a small difference in spelling. This is a support and maintenance nightmare, so let’s make sure we prevent it.

The nice thing about a scenario outline is that it’s very easy to add more examples. We can just append the following two cases to the table:

| Steve123           | steve2@yahoo.com  | Username taken            |
| steve456           | Steve@Yahoo.com   | Email already registered  |

If we really want to avoid support problems with accounts that are too similar, perhaps we should also prevent people from registering usernames that only differ from existing accounts in interpunction symbols.

| steve.456          | Steve3@Yahoo.com   | Username taken  |
| steve_456          | Steve3@Yahoo.com   | Username taken  |

Discussing boundaries often leads to more examples, and identifying hidden domain rules. And again, some systems will have different rules. Expose an example, discuss it, then decide if you want to keep it or not. Perhaps the interpunction checking in usernames is too much or too complicated, so we can skip that for now. We could monitor if it becomes a problem, and add it to a later iteration if necessary. In most teams, it’s up to developers and testers to offer this for discussion, and for business representatives to decide on priority and scoping.

Even if the username interpunction examples for usernames end up out of scope, thinking about this them can lead you to consider similar boundaries for other input fields. Should we care about interpunction symbols in emails?

For example, Gmail allows users to put a dot anywhere in the email, or follow the email with a plus sign and a label, so “steve.o@gmail.com” and “steveo@gmail.com” are actually the same physical account, as well as “steveo+anything@gmail.com”. Popular email systems often have aliases, so “steve@gmail.com” is the same as “steve@googlemail.com”. How much do we care about preventing duplicated emails? Should we try to fight against commonly known cases such as that, or just ignore them? These are very specific domain questions, and the answers will depend on the risks you are trying to control. For a complex set of rules, there may be many more examples, and this table might become too big to read.

The next challenge

This brings us to the challenge for the next week. Let’s say that we actually do want to be a bit more strict about preventing the same person from registering twice, so all those Gmail hacks need to be handled. Plus the support managers came back and said that they liked the idea of avoiding problems with usernames that are too similar. This will lead to dozens, if not hundreds of examples. Alister also looks at examples around password complexity, which is another common aspect of registration that we might want to add to the registration feature. Putting it all into a single scenario outline is definitely not a good idea. How would you better structure such a big list of examples, so it’s easy to understand and maintain?

Next week, we’ll publish an analysis of the responses and a proposed solution, along with some ideas on handling automation and avoiding database performance issues.

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the challenge with your friends and team members by clicking on one of the social icons below 👇

The post Solving “How to set up something that’s not supposed to exist?” #GivenWhenThenWithStyle appeared first on SpecFlow - Cucumber for .NET.

We are simplifying our SpecFlow and .NET version support!

$
0
0

SpecFlow and SpecFlow+ Runner Versioning

We hear you and have to admit that it is currently difficult to know which version of the SpecFlow+ Runner is compatible with which SpecFlow version. Therefore, we are changing the SpecFlow versions that are supported by the SpecFlow+ Runner.

To make the version compatibility easier to understand, setup or upgrade a project, we have made the following changes:

  • From SpecFlow 3.3 onwards SpecFlow and SpecFlow+ Runner will have the same Major.Minor version number
  • From SpecFlow+ Runner 3.3 onwards SpecFlow+ Runner supports only the latest SpecFlow version

That is also the reason why we are jumping straight from SpecFlow 3.1 to 3.3. To use the SpecFlow+ Runner with earlier versions of SpecFlow, the old versions are still available on NuGet.org.

.NET supported Versions

From SpecFlow 3.3 onwards we are changing our supported .NET versions. This hasn’t been changed since SpecFlow 2.0 back in 2015.

.NET Core

For .NET Core we are now following the Microsoft .NET Core Support policy. This means from SpecFlow 3.3 onwards we will support the current supported versions from Microsoft .NET Core 2.1 and .NET Core 3.1.

.NET Framework

We are changing the minimum supported version from .NET Framework 4.5 to .NET Framework 4.6.1. Microsoft already stopped the support for .NET Framework 4.5 back in 2016.

If you still need support for older version of .NET Core and .NET Framework, you can still use older versions of SpecFlow and SpecFlow+ Runner from NuGet.org.

If you have any problems with upgrading to the latest versions of SpecFlow and SpecFlow+ Runner or if you want provide feedback to our versioning changes, please let us know at support@specflow.org!

The post We are simplifying our SpecFlow and .NET version support! appeared first on SpecFlow - Cucumber for .NET.

Working with BDD Remote Part 1 – Collect Examples Online

$
0
0

Even as we slowly open offices, remote work remains the norm. The sudden shift to remote work clearly exposed the importance – and challenges – of remote collaboration. Now that a few months of experience has helped us understand the scope of the challenge, it is a great time to consider how we can use methodologies like BDD to improve remote collaboration.

Collaboration is a value that is frequently emphasized in agile software projects. However, in many cases, the concrete actions stop at creating cross-functional teams (including both devs and testers), inviting more people to the meetings or accommodating everyone in the same room. Unfortunately, this is very rarely enough to allow true collaboration to unfold. True collaboration focuses on people working with each other, not just working next to each other. In a remote environment, this difference is exacerbated. When social interactions are limited, problems can remain hidden for a much longer time.

To achieve collaboration in remote environments , some sort of facilitation is necessary. There are plenty of ways to facilitate collaboration; in this article we are focusing on how teams can collaborate in understanding, documenting and verifying business requirements in remote environments. Production issues, change requests and rework commonly originate from requirements. Think about ambiguous, missing or unclear requirements, but also about conflicts caused by different assumptions! Using BDD for online distributed teams can reduce the probability of these issues by facilitating collaboration around requirements. 

In this series, we are going to focus on three areas of remote collaboration related to BDD:

  • Part 1 – Collecting examples online: How the example guided specification approach can be used in remote environments to build a shared understanding
  • Part 2 – Driving development with Gherkin: How the expected behaviour can be efficiently documented using the Gherkin syntax so that it provides an exact guidance for remote team members working in a distributed setting
  • Part 3 – Making living documentation accessible: How you can improve remote collaboration by making the BDD scenarios and their status easily accessible for the team members and other stakeholders

Collect examples online

In remote environments, feedback loops are generally longer because people work alone more…and longer. As a result, the impact of requirement-related issues are amplified. BDD enables collaboration on the requirements before the actual development starts. It does this using the concept of specification by examples.

Concrete examples help; discussions lead to a better and shared understanding of the requirements. This approach is a natural way of learning that puts the concepts represented by the requirements into context. Discussing the requirements with the help of examples in a project applying BDD is often referred to as the “Discovery” practice.

Due to its nature, the discovery practice works best when the team discusses the requirements together, on an online meeting. The discussion of a user story usually generates 5 to 20 illustrative examples. Without any live discussion, either the feedback loops are too long or the examples become the written specification (with its usual problems). 

Collecting examples can be started by focusing on simple questions, like “Could you please give an example for this?” However, the best practice is to consider facilitation  techniques like Example Mapping or Feature Mapping. During the discovery session, we collect examples for the user story being discussed. The mentioned techniques further improve collaboration by providing a good (visual/spatial) overview of the problem and allowing people to collaborate using different techniques (reading, writing, arranging, etc.) besides talking. This can be achieved by capturing the examples on virtual cards and arranging them logically.

An example map (source: “Discovery” book)

These conversation techniques can be easily adapted to a remote setup with the help of tools that allow creating and arranging virtual cards, e.g. Miro, Google Sheets or Excel for the web. These tools support collaborative editing (simultaneous editing by multiple participants) and allow participants to drill down to get a better overview or to focus on  particular details. (A freely-available Google Sheets / Excel template for example mapping can be downloaded here.) I have also heard about teams doing discovery workshops with mind mapping tools or even with a simple hierarchical list.

 Example maps in different online formats (source: Gaspar Nagy)

A remote discovery session is usually organized as an online meeting a few times a week. The product owner (PO) prepares the discussion of the user story by collecting the acceptance criteria (rules). They can prepare a few key examples as well, but I usually recommend against  “presenting” these examples so that the team can discover them instead. Otherwise, the meeting quickly falls into a presentation. Remember, we want to facilitate collaboration! 

During the online discovery meeting, the team can nominate a facilitator for collecting and capturing the examples. The facilitation is not a difficult job; anyone can do it. I recommend choosing a different facilitator for each occasion. It fosters team spirit and the discussions become more engaging. You can find further tips for facilitators in this post.

Our goal with collecting examples is to establish a shared understanding. When working with BDD, these examples can later become BDD scenarios. (The majority of them usually do.) Even though we know we will use these examples as scenarios, usually we don’t capture them in Given/When/Then style. A properly phrased scenario requires additional considerations, like proper grammatical structures, the review of existing steps, or the use of precise terminology. While all these things are important, doing them at the discovery workshop is not efficient and breaks the creative thinking we would like to focus on. Spend the precious time when everyone is together at the online meeting for those discussions that cannot be distributed.

At this stage, it is perfectly acceptable to capture the examples as a list of steps that describe the behavior of the system in a particular case. It might help if we separate the context, the action, and the outcome parts of these examples though.

An example collected during a discovery session (source: Gaspar Nagy)

You can let the facilitator put down the examples, but you can also let other team members help: Whoever has come up with the example can capture it in the chosen online tool. It also makes sense to capture any open questions or assumptions. All artifacts should be shared and owned by the entire team.

By the end of the meeting, all rules should have been discussed and clarified, as well as illustrated with a couple of examples. At this point, the team will have the necessary confidence to start working with the story.

In the next part…

In this part, we have seen how the example guided specification approach can be used in a remote environment. We have seen what an online discovery session looks like and how it helps to ensure a shared understanding.

During the discovery session, we are collecting examples that capture a particular usage of the application in a rough way. We did not want to capture them as BDD scenarios because that would distract our brainstorming.

In Part 2 we will see how we can properly document our rough examples using the Gherkin format with the Given/When/Then keywords. This allows them to drive the implementation of the described feature.


Gaspar Nagy is working as a BDD trainer & coach. Check out his public BDD with SpecFlow, BDD Vitals or BDD with Cucumber courses or request an in-house private course for your team. He is a BDD addict and as such he’s editing a monthly newsletter about interesting articles, videos and news related to BDD, SpecFlow and Cucumber.

The post Working with BDD Remote Part 1 – Collect Examples Online appeared first on SpecFlow - Cucumber for .NET.

Solving: How to structure a long list of examples? #GivenWhenThenWithStyle

$
0
0

Last week, we cleaned up a vague scenario, transforming it into a relatively long list of concrete examples. The challenge for this week was to provide a good structure to capture all those examples in a feature file.

A large set of varied examples can be overwhelming. Examples that show important boundaries usually differ only in small details, so in a long list of examples many will contain similar information. Unfortunately, showing a lot of similar scenarios with only minor differences will make readers tune out and stop seeing the details, which is exactly the opposite of what we want to achieve with a good feature file.

Usually, a big part of the repetitive data comes from common preconditions. Developers often try to clean up such scenarios by applying programming design guidelines, such as DRY (“don’t repeat yourself”). They will remove all the duplication to achieve something akin to a normalised data set. This makes it easy to maintain and evolve the examples in the feature files, but it also makes it very difficult to understand individual scenarios. Readers have to remember too many things spread throughout a file to fully grasp the context of a single test case.

The key to capturing long lists of examples in a feature file is to find a good balance between clarity and completeness, between repeating contextual information and hiding it. Repeating the contextual information close to where it is used helps with understanding. On the other hand, repeating too many things too often makes it difficult to see the key differences (and makes it difficult to update the examples consistently). Here are four ways to restructure long lists of examples into something easy to understand and easy to maintain.

Identify groups of examples

For me, the first step when restructuring a long list of examples is usually to identify meaningful groups among them. Analyse common aspects to identify groups, then focus on clearly showing variation in each group.

Discovering meaningful groups of examples that have a lot of things in common allows us to just enough context to understand each group, allowing information to be duplicated across contexts, but focus examples in each group so they show differences between them clearly. There’s no hard rule how many examples should go into each group, but in my experience anywhere between three and 10 is fine. If a group contains more than ten examples, then we start having the initial problem again within that group, where readers start to tune out. Fewer than three examples usually gets me to ask if we covered enough boundaries to show a rule.

Each group should have a specific scope. On a very coarse level, groups can demonstrate a specific business rule. If there are too many examples to demonstrate a business rule fully, then structure groups around answering individual questions about that rule. If there are too many examples to fully answer a specific question, create groups around demonstrating a specific boundary or problem type.

Example mapping, a scoping technique for collaborative analysis promoted by Matt Wynne from the Cucumber team, turns this idea of groups of examples into a fully fledged facilitation technique. It starts by creating a breakdown of scope into topics, questions and groups of examples. If you start with example mapping, the map itself may provide good hints about grouping examples. You may still want to restructure individual groups if they end up too big.

Capture the variations

Within each group, try identifying the things that really change. Scenario outlines, mentioned in the previous post, are a nice way of dividing data from examples into two parts. One part will be common for the whole group, and you can specify it in the Given/When/Then section of the scenario outline. Another part of the data shows what’s really different between individual examples, and you can specify it within the Examples block, following the Given/When/Then.

As an illustration, when demonstrating rules around detecting duplicated emails during registration, we might want to show some more contextual information. To avoid potential bad assumptions about other types of errors (such as duplicated username), we might also want to show the proposed username and the name of the person registering. That information is not critical for each individual example, but it helps to understand the context. Instead of repeating it for each case, we can capture once for each group, in the When section.

Scenario Outline: duplicated emails should be prevented

   Email is case insensitive. GMail is a very popular system so 
   many users will register with gmail emails. Sometimes they use 
   gmail aliases or labels. 
   To prevent users mistakenly registering for multiple accounts
   the user repository should recognise common Gmail tricks.

Given a user repository with the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
When the Steve James tries to register with username steve123 and <email>
Then the registration should fail with <registration message> 

Examples:

| email               | registration message      | 
| mi.ke@gmail.com     | Email already registered  |
| mike+test@gmail.com | Email already registered  |
| Mike@gmail.com      | Email already registered  |
| mike@Gmail.com      | Email already registered  |
| mike@googlemail.com | Email already registered  |

Notice that in this case the message is always the same, so there isn’t much point in repeating it. A table column that always has the same value is a good hint that it can be removed. A table column that only has two values in a group of 10 examples perhaps suggests that the group should be split into two sets of five examples.

We can move the values that are always the same to the common part of the scenario outline:

Given a user repository with the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
When the Steve James tries to register with username steve123 and <email>
Then the registration should fail with "Email already registered"

Examples:

| email               | 
| mi.ke@gmail.com     | 
| mike+test@gmail.com |
| Mike@gmail.com      |
| mike@Gmail.com      |
| mike@googlemail.com |

This set of examples is concise, but perhaps it’s too brief. It’s not clear why these examples need to be in the spec. With the earlier set of examples, the registration message helped to explain what’s going on. But the messages were the same for all these examples, so they were not pointing at differences between examples. We had to explain that in the scenario context, which is good, but can do even better. When there’s nothing obvious in the domain to serve that purpose, give each example a meaningful name. You can, for example, introduce a comment column that will be ignored by test automation:

Given a user repository with the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
When the Steve James tries to register with username steve123 and <email>
Then the registration should fail with "Email already registered"

Examples:

| comment                                                    | email               |
| google ignores dots in an email, so mi.ke is equal to mike | mi.ke@gmail.com     |
| google allows setting labels by adding +label to an email  | mike+test@gmail.com |
| emails should be case insensitive                          | Mike@gmail.com      |                           
| domains should be case insensitive                         | mike@Gmail.com      | 
| googlemail is a equivalent alias for gmail                 | mike@googlemail.com | 

Identifying meaningful groups, and then structuring the examples into scenario outlines based on those groups, allows us to provide just enough context for understanding each group. It also allows us to use different structure for each set of examples. The scenario outlines around duplicated usernames will have different When and Then clauses (perhaps using a common email to show context). The examples themselves in that group would show variations in usernames. The When and Then steps will look similar to the ones we used previously, but likely use different placeholders from the examples that demonstrate email rules. The scenario outlines for password validity checks will have a totally different structure – perhaps not even showing the personal name and email.

Another useful trick to keep in mind with scenario outlines is that a single outline can have many example groups. If the list of examples around duplicated emails becomes too big, you can just split it into several groups. When doing this, I like to add a title to each group of examples, to show its scope. This allows us to ask further questions and identify further examples. For example, we only have two examples around case sensitivity. Thinking a bit harder about additional edge cases for that rule, we can start considering unicode problems. Unicode normalisation tricks can allow people to spoof data easily, abusing lowercase transformations in back-end components, and we might want to add a few examples to ensure this is taken care of.

Here is what an evolved scenario outline could look like:

Given a user repository with the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
When the Steve James tries to register with username steve123 and <email>
Then the registration should fail with "Email already registered"

Examples: uppercase/lowercase aliases should be detected

| comment                                                    | email               |
| emails should be case insensitive                          | Mike@yahoo.com      |
| domains should be case insensitive                         | mike@Yahoo.com      |
| unicode normalisation tricks should be detected            | mᴵke@yahoo.com      |

Examples: gmail aliases should be detected

| comment                                                    | email               |
| google ignores dots in an email, so mi.ke is equal to mike | mi.ke@gmail.com     |
| google allows setting labels by adding +label to an email  | mike+test@gmail.com |
| googlemail is a equivalent alias for gmail                 | mike@googlemail.com |

Use a framing example

As we start discussing more specific rules around preventing duplication, the structure of examples will change to reflect that. Each set of examples may have a different structure, focused on the key aspects that it is trying to show. That’s how you can avoid overly complex examples and tables with too much data. However, explaining a single feature through lots of different scenario outlines with varying structures might make things difficult to grasp at first. To make a feature file easy to understand, I like to use a framing scenario first. That scenario should be simple in terms of domain rules, and it should not try to explain difficult boundaries. It’s there to help readers understand the structure, not to prevent problems. A good choice is usually a “happy day” case that demonstrates the common flow by showing the full structure of data. For example, this could be the successful registration case. The framing scenario can then be followed by increasingly complex scenarios or scenario outlines. For example, I would first list the generic email rules, then follow that with system-specific rules such as the ones for GMail.

Feature: Preventing duplicated registrations

Scenario: Successful registration
...
Scenario: allowing duplicated personal names
...
Scenario outline: preventing duplicated emails
...
Scenario outline: preventing GMail aliases
...

(Maybe) extract common preconditions into a background

With the framing scenario structure, you will sometimes find preconditions shared among all scenarios. In this case, the initial users in the repository might be the same for all examples. In cases such as that, you have an option to avoid duplication and move common preconditions from individual scenarios to a Background section. Automation tools will copy the steps from the background section before each individual scenario when they execute them.

When doing this, beware of hiding too much. The background section is useful only when the actual data is relatively simple so people can remember it. A common pitfall with using feature file backgrounds is that it becomes quite complex and readers lose the understanding of the feature before they even get to the interesting part. If you can’t keep the common background vert simple, it’s perhaps worth introducing just the minimum required inputs within each scenario outline itself.

Here is how a fully structured feature file, with a background section and a framing scenario, would look like:

Feature: Preventing duplicated registrations

   To prevent users mistakenly registering for multiple accounts
   the user repository should reject registrations matching
   existing users

Background:

Given a user repository with the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
| steve123 | Steve James   | steve@yahoo.com  |

Scenario: Users with unique data should be able to register

When John Michaels attempts to register with the username "john" and email "johnm@gmail.com"
Then the user repository will contain the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
| steve123 | Steve James   | steve@yahoo.com  |
| john     | John Michaels | johnm@gmail.com  |

Scenario: Personal names do not have to be unique

When Mike Smith attempts to register with the username "john" and email "johnm@gmail.com"
Then the user repository will contain the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
| steve123 | Steve James   | steve@yahoo.com  |
| john     | Mike Smith    | johnm@gmail.com  |

Scenario Outline: Usernames should be unique

  Detecting simple duplication is not enough, since usernames that are visually
  similar may lead to support problems and security issues. See 
  https://engineering.atspotify.com/2013/06/18/creative-usernames/ for more information.

When the Steve James tries to register with <requested username> and "steve5@gmail.com"
Then the registration should fail with "Username taken"
And user repository should contain the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
| steve123 | Steve James   | steve@yahoo.com  |

Examples:

| comment                     | requested username | 
| identical username          | steve123           |
| minor spelling difference   | Steve123           |
| unicode normalisation       | sᴛᴇᴠᴇ123           |
| interpunction difference    | steve123.          |

Scenario Outline: Duplicated emails should be disallowed

Given a user repository with the following users:
| username | personal name | email            |
| mike     | Mike Smith    | mike@gmail.com   |
When the Steve James tries to register with username steve123 and <email>
Then the registration should fail with "Email already registered"

Examples: uppercase/lowercase aliases should be detected

| comment                                                    | email               |
| emails should be case insensitive                          | Mike@yahoo.com      |
| domains should be case insensitive                         | mike@Yahoo.com      |
| unicode normalisation tricks should be detected            | mᴵke@yahoo.com      |

Examples: Gmail aliases should be detected

   GMail is a very popular system so many users will register
   with gmail emails. Sometimes they use gmail aliases or labels,
   to prevent users mistakenly registering for multiple accounts
   the user repository should recognise common Gmail tricks.

| comment                                                    | email               |
| google ignores dots in an email, so mi.ke is equal to mike | mi.ke@gmail.com     |
| google allows setting labels by adding +label to an email  | mike+test@gmail.com |
| googlemail is a equivalent alias for gmail                 | mike@googlemail.com |

The next challenge

The challenge for this week is a bit more technical – dealing with situations that require waiting.

Check out the challenge

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the challenge with your friends and team members by clicking on one of the social icons below 👇

The post Solving: How to structure a long list of examples? #GivenWhenThenWithStyle appeared first on SpecFlow - Cucumber for .NET.

SpecFlow & SpecFlow+Runner 3.3 Are Available!

$
0
0

Yes, you are reading correctly. We are jumping directly from SpecFlow 3.1 to SpecFlow 3.3. This is to make it easier to understand, which version of SpecFlow works with which version of SpecFlow+ Runner. More details can be found here.

What is new?

This release of SpecFlow and SpecFlow+ Runner combines a lot of small and medium fixes, features, and performance increases. For a complete list have a look at the detailed changelog at the end.

There were two bigger changes, that made it necessary to increase the version number to 3.3.

Support for .NET Core SDK 3.1.200 and later

Because of some behavior changes in the .NET Core SDK 3.1.200 and later, we had to made adjustments to our Assembly loading logic, that SpecFlow is working with it again.

Supported .NET Versions

Starting with this version, we are supporting .NET Framework 4.6.1 and later and are following the Microsoft .NET Core Support policy. You can read more about this in this blog post.

This means, we support:

  • >= .NET Framework 4.6.1
  • .NET Core 2.1
  • .NET Core 3.1

Thanks to all our amazing contributors!

This release would not be possible without you.

These are contributors to SpecFlow since the last 3.1 release

If you didn’t already, you can get some stickers from us here (When we are back in the office, because there are all our stickers stored).

Detailed Changelog

SpecFlow

Features:

API Changes:

  • Now allows override of method categories in NUnit to allow for plugin development on this function
  • (breaking) tableRow.GetEnum(..) has now a class contraint on T
  • (breaking) tableRow.GetTheEnumValue(..) has now a class contraint on T
  • added tableRow.GetDiscreteEnum
  • TechTalk.SpecFlow.Assist.ValueRetrievers.* public classes have undergone some refactoring. IValueRetriever interface remains unchanged.

Changes:

  • Performance and memory improvements for table.CreateSet and table.CreateInstance
  • Improved performance of ValueRetrievers
  • Removed Nullable*ValueRetriever as nullability is now handled by the *ValueRetriever directly.
  • Changed all specflow.org hyperlinks to https (docs, generated code, packages)
  • Changed minimal .NET Framework version from .NET 4.5 to .NET 4.6.1
  • Changed minimal .NET Framework version of the Generator plugins and the SpecFlow.Tools.MSBuild.Generation package from .NET 4.7.1 to .NET 4.6.1
  • Downgrade to be able to use xUnit 2.4.0 (because VS Templates are using this version)

Fixes:

  • Empty value for nullable enum should not throw an exception
  • RegEx performance fix in TEHelpers (Table column name validation)
  • Adjust assembly loading of Generator plugins to changed behavior in .NET Core SDK 3.1.200 and later – https://github.com/SpecFlowOSS/SpecFlow/issues/1912
  • add auto-generated comment for StyleCop to ignore the Assemblyhook files

Fixes for Development:

  • Changed unit tests to ignore custom user overrides to data formatting in en-US locale
  • Some errors could randomly happen when running tests in parallel

SpecFlow+ Runner

Features:

  • Support for SpecFlow 3.3

Bugfixes:

The post SpecFlow & SpecFlow+Runner 3.3 Are Available! appeared first on SpecFlow - Cucumber for .NET.

How to deal with pauses and timeouts? #GivenWhenThenWithStyle

$
0
0

The challenge for this week is dealing with wait statements and pauses in specifications.

My testers keep adding pauses into Given-When-Then. They claim it’s necessary because of how the tests work. I don’t like it because the numbers look random and do not reflect our business rules. Also, sometimes pauses aren’t long enough. The tests sometimes fail although there are no bugs in the system version we’re testing, because the pauses aren’t long enough. How do we avoid that?

This problem is symptomatic of working with an asynchronous process, often an external system or an executable outside of your immediate control. For example, think of an e-commerce platform that passes orders through a risk evaluation module before confirming. The risk check might take a few seconds, and the user may not be able to pay until the order is confirmed. In a visual user interface, this waiting period is often shown with a spinner or a progress bar. A bad, but common way of capturing this in Given-When-Then is to add a pause between actions:

Given a user checks out with a shopping cart worth $200
When the user submits an order 
And the system waits 3 seconds
And the user authorises the payment
Then ...

In some cases, it’s not the system under test requiring pauses, but the way a test is automated. A common example is executing tests through a web browser automation tool such as Selenium or Puppeteer. The framework can load a web page using a browser, but that page might need to fetch external javascript files or additional content to fully initialise the user interface. Triggering an action immediately after a web page loads might cause the test to fail, because the related elements are not yet available. Adding pauses to Given-When-Then scenarios is also a common work-around for these kinds of issues:

Given a user registers successfully
When the account page reloads
And the user waits 2 seconds
Then the account page displays "Account approved"

In both these situations the testing process requires a bit of time between an action and observing its result. But the actual period will vary from test to test, even between two executions of the same test. It will depend on network latency, CPU load and the amount of data required to process. That is why it’s not possible to correctly select the period duration upfront. Choosing a short pause causes tests to occasionally fail because the asynchronous process sometimes does not complete when the test framework moves on to the next step, so people have to waste time chasing ghost problems. Setting a pause that’s long delays feedback and slows down testing excessively.

How would you rephrase and restructure this instead? Post your suggestions using the link below. If you’d like to send a longer comment, perhaps write a blog post and then just send us the URL (you can do that using the same form, link below).

Read-on for a selection of good ideas we’ve received and our suggestions on how to handle scenarios such as this one.

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the challenge with your friends and team members by clicking on one of the social icons below 👇

The post How to deal with pauses and timeouts? #GivenWhenThenWithStyle appeared first on SpecFlow - Cucumber for .NET.

Solving: How to deal with pauses and timeouts? #GivenWhenThenWithStyle

$
0
0

The challenge for last week was to improve specifications that deal with pauses, in particular those that wait for a period of time. For a detailed explanation of the problem, check out the original post. This article contains an analysis of the community responses, two ways of cleaning up the problematic specs, and tips on more general approaches to solving similar problems.

Some people noted that Given-When-Then tools aren’t the right solution for testing systems requiring synchronization. However, issues with waiting and synchronization are general problems of test design, not something specific to Given-When-Then tools. The design ideas outlined in this article are applicable to other classes of tools, and other types of tests as well. However, there is one specific aspect of Given-When-Then that is important to consider first: where to define the pauses.

Move waiting into step implementations

Lada Flac, commenting on the problem of asynchronous web page elements, suggested pushing the pauses into step implementations:

“I don’t see a need to add the waits to gherkin steps. I would add them only to the implementation steps. And those would not be implicit waits, but those where a script would try to find an element until the timeout.”

Lada is spot on. The most common cause for waiting in tests is the way a test is executed. For example, testing through a browser, or over a network, implies asynchronous network operations so it may require synchronisation. In cases such as that, it’s best to place the waiting in the step implementations, not the scenario definition. Describing a flexible periodic polling process with exponential back-off is huge challenge using Given-When-Then, but it’s very easy to do with C#, Java or any other programming language.

Kuba Nowak suggested a similar way to rephrase the waiting, using steps such as “And waits until payment page opens”, and then dealing with the meaning of “opens” within the step implementation.

Moving the waiting into step implementations makes the scenario definitions shorter and more focused on the problem domain. A team can discuss such scenarios more easily with business representatives. By moving the pauses into step implementations, as Lada and Kuba suggest, we can also postpone the decision on how to perform them. This gives us the option to avoid waiting for time. Jonathan Timm nicely explained it:

Regardless of the tool, automating user interaction needs to be a two-way conversation between the application under test and the automation code. Human users take cues from user interfaces unconsciously, and the same cues can be listened for with code using existing native functionality in tools like Selenium.

Jonathan noted that WebDriver, a popular user interface automation tool, supports waiting until an element becomes visible or clickable. Instead of pausing for a specified period, the implementation of a step can pause until some interface elements appear or become active.

Whenever possible, use this trick: instead of waiting for time, try waiting for events. Pausing for a pre-defined period should be the technique of last resort, applied only when there are no other ways of handling the synchronization.

Wait for events, not for time

David Wardlaw wrote a nice blog post with his thinking about the problem, documenting several ideas with increasing complexity. One of the very important things he noticed is that waiting for time is generally not a good idea since the background process can depend on many different factors. David wrote: “The timing can be affected by things like CPU usage and network traffic and you may get inconsistent test results”. This is especially true for test environments, which are usually underpowered, and sometimes shared across teams.

Stan Desyatnikov proposed a way to rephrase the waiting scenarios. “Don’t reflect delays (of the application under testing) in steps of scenarios… Wait for a particular condition to be met…”. Most of the responses to this challenge correctly identified some other condition for waiting, often related to the user interface. David Wardlaw suggested looking at the user experience, for example specifying the condition “When the account page loading progress bar is at 100%”.

Faith Peterson wrote a very detailed blog response with excellent ideas, proposing to “raise the level of abstraction and ignore the pause”. Faith explains it:

“This has worked for me when my primary interest is verifying the human-observable result of an integrated system, and I’m less interested in verifying internal operations or handoffs.”

I fully agree with Faith, but it’s important to note that this trick can work for a much wider set of contexts than just human-observable results. As Jonathan noted, people subconsciously take cues from a user interface, so it’s easy to think about human-observable results. But we can, and in most cases should, raise the level of abstraction even higher. Mathieu Roseboom looked in this problem from the perspective of relevance:

“The fact that a step needs to wait for a certain amount of time should not be part of the scenario in my opinion. It is mostly (there are always exceptions) not relevant for the business.”

A progress bar reaching a certain percentage or a page loading fully is definitely better than specifying a pause, but it’s still implementation detail, not a core business requirement. Combining Faith’s idea about raising abstractions and Mathieu’s idea of relevance into work, Dave Nicolette suggested the following improvement to the first scenario in last week’s challenge:

Given a user has $200 worth of goods in their cart 
When the user completes the purchase 
Then..

Dalibor Karlović suggested replacing the waiting statements in the second scenario with a meaningful business event, step such as:

And the account verification completes

Both these suggestions frame the waiting period in terms of an event from the business domain, not user interface.

So how do we decide if the condition for waiting should be described in terms of user interface elements, some more generic user observable behaviour or a business process? The answer is in one of the most useful techniques for clarifying scenarios: focus on what, not on how.

Focus on what, not how

There are usually two dimensions of relevance in Given-When-Then scenarios and related tests. The first is the purpose of a test, the second is the process of testing. For example, in a specification of the registration process, observing the progress bar is not relevant for the purpose of a test. It may be relevant for the process of testing. On the other hand, in a specification for user interface interactions, the progress bar activity is relevant for the purpose of the test as well.

When scenarios focus on how something should be tested rather than what a feature should do, then the purpose of a feature is obscured, and the scenario depends too much on a specific implementation or system constraints. When the implementation changes, or when the code executes on a different system, tests based on those specifications often start to fail although there are no bugs in the underlying code. As a general guideline, try to keep the scenario definitions focused on things relevant for the purpose of the test, not on the mechanics of test execution. Move the mechanics into step implementations, into the automation layer.

Sometimes, the distinction can be very subtle. For example, Stan Desyatnikov proposed restructuring the second scenario from the challenge in the following way:

When a user registers successfully
Then the account page displays "Account approved"

In cases such as this one, it’s interesting to consider whether the account page is relevant for the purpose of the test, or is it just there to explain the process of testing something. We could rephrase the post-condition as just one line:

Then the user account status is "approved"

By specifying the wait condition in the terminology of the business domain, instead of test execution, we can postpone the discussion on test automation. The scenarios will be clearer and easier to discuss with business representatives. We can potentially optimise the execution later so it can go below the user interface, or even avoid asynchronous issues altogether.

Bring the interactions into the model

Time-based pauses in scenarios are not necessarily symptoms of the test process leaking into specifications. They could also be caused by a wrong model, which in turn causes bad user experience and makes the system error prone. Any web site that warns against pressing the back button during a background process is a good example of this. The worst offenders are airline ticket sites with warnings that you mustn’t close the browser window during payment. Why not? As if the user watching some web progress bar is magically going to improve payment approval rates. Connection problems are a fact of life on the Internet, especially with consumer applications. Wi-Fi signals drop, phone batteries die, and people close windows by mistake.

Showing a warning against something users can’t control doesn’t make the problem disappear. When an asynchronous process is fundamental for the purpose of the feature, not just for the process of testing, then we don’t want to hide it into step definition code. This is not accidental technical complexity, it’s a fundamental property of the problem domain. We want to expose such domain properties, so we can openly discuss them and define what should happen when the situation develops in a predictable but unwanted way.

René Busch suggested rephrasing the registration scenario in terms of events, such as the one below:

Given the process of registration is running
when the registration process completes successfully
then the user gets a notification the registration is completed successfully
then the user can confirm that registration

Given the process of registration is running
when the registration process completes in error 
then the user gets a notification the registration is completed with failure
then the user can do/see ....

The original challenge had two example scenarios. The second was stuck in waiting purely because of badly described user interface constraints. But the first one, dealing with order approvals, is a lot more tricky. In the challenge post, I explained that “platform passes orders through a risk evaluation module before confirming”. This is a hint that there might be a fundamentally asynchronous problem in this domain. For example, although an automated risk evaluation module might take a few seconds most of the time, fraud prevention can also require escalating to a human investigator, which might take hours or days.

In such cases, the events and notifications are what we need to test, not how we’re testing something. They need to be explicitly defined and included in the scenarios. This allows us to specify examples when the risk evaluation is not yet complete, without worrying how long the process actually takes. Treating the process as fundamentally asynchronous allows us to design better user notifications, improve user experience, system operations and support.

If a risk review can take a while, there is a good chance that a user will want to view the status of an order during that process, or make new orders. Identifying such important domain events might lead to further refinement of the underlying software model, and asking more interesting questions. For example, is the risk assessment the only thing that happens before an order is confirmed? Perhaps there are other things that need to happen, such as checking the inventory. Alternatively, we can start asking questions around what happens when the order confirmation is pending, and when the order confirmation resulted in a negative response. This might lead us to discover some further events, such as the order being rejected. We might need some additional examples around that. Discovering domain events is key to modelling asynchronous systems correctly. For some further background on this, check out Alberto Brandolini’s work on Event Storming.

Similarly to how identifying user interface events (such as a button becoming visible) helps to automate tests better with UI drivers, identifying domain events is important because it allows us to introduce additional control points into the system. René’s example shows this nicely. Instead of actually running an external registration process, the test framework can just submit the appropriate results synchronously. Such tests will run faster and more reliably. We can also avoid the complexity of setting up data for an external system, and keeping it up to date as the external system changes. In the best case scenario, this can also turn a test case that was previously asynchronous into something that can run synchronously. Of course, it’s good to complement this with a proper integration test which shows that the system under test and the registration process communicate correctly, but this is a technical concern that should be handled outside Given-When-Then feature files.

Modelling time

Specifying what and automating how something is done is a great technique to decide if something should go into scenario definitions or step implementations. If what we’re specifying is user interface, then user interface interactions should stay in the scenario definition. If what we’re specifying is a business process, and the user interface is just how we’re testing it, then user interface interactions should go into the step implementations. This also applies to time.

Most responses to this challenge correctly suggested moving away from specific time periods, but there are cases where the passing of time is actually what we’re testing. David Wardlaw had one such example in his post, specifying that user registration must complete within three minutes, otherwise the process should fail and users need to be notified about an external system not being available. Performance requirements, service level agreements and operational constraints often involve specific time limits, and in such cases the period itself needs to be visible in the scenario. This will allow us to discuss other examples, probe for hidden assumptions and change rules in the future more easily.

However, even in such cases, it is usually wrong to implement the test mechanics by waiting for a period of time. With longer periods, such as checking that a regulatory report is generated at the end of every quarter or that transactions are reconciled at the end of the business day, people tend to avoid automating the tests at all because they don’t know how to handle the waits. With short periods, such as seconds or minutes, sometimes people are tempted to just block the test and wait it out. Please don’t. That makes testing unnecessarily slow.

If time is critical to an aspect of your system, model it in the domain and represent it as business time. Technically, this often involves creating a wrapper around the system clock, with methods to schedule and wait for events. Anything else that depends on time should not use the system clock, but connect to the business clock instead. That allows the test system to easily move forwards and backwards in time and prove complex business rules, without delaying the test execution. This is the right approach for dealing with time-based events, such as expiring unpaid accounts if more than two hours passed since registration, generating end of day or end of quarter reports, or generating monthly statements. We can easily move forward a whole month, or stop just one second short of a month start, to prove that the right things happened or did not happen.

When a business clock runs the show, there is only one time definition, so everything can be synchronous to that clock. This often means that test automation turns to synchronous execution, which is much more reliable and resilient than working with asynchronous events.

The next challenge

Check back tomorrow for the next challenge in this series.

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the solution with your friends and team members by clicking on one of the social icons below👇

The post Solving: How to deal with pauses and timeouts? #GivenWhenThenWithStyle appeared first on SpecFlow - Cucumber for .NET.


Should the feature description look like a user story? #GivenWhenThenWithStyle

$
0
0

The challenge for this week deals with a common way of structuring feature file headers and scenario headers.

We write feature introductions in the user story format (As a… I want… So that…). Then we add scenarios related to different user personas and different user needs, and the original description no longer fits the contents. As the feature file grows, this becomes more and more wrong. Any ideas on structuring the story at the top to be more generic, but not too vague?

Gherkin, the format of Given-When-Then files, allows users to add any text after a scenario or a feature title, and just ignores it from an automation perspective. This is incredibly useful to provide more context for readers. Most online examples show the feature description in the Connextra user story format, such as the one below:

Feature: pending invoices report

  As a client account manager, 
  I want a report showing all pending (issued but unpaid) invoices for a client
  So that I can control control client credit risk


This usually works well when a feature is first introduced, since any new functionality should come with a clearly associated user need. But as more stories extend the feature, the connection between the feature and the stories becomes complicated.

For example, we might initially add a pending invoice report because an accounts manager wants it, in order to control client credit risk. But as the system grows, and we might add features to the pending invoice report that help accounts managers achieve other goals. A regular customer might be eligible for a discount, so client managers might want to know quarterly subtotals and averages to decide on discount amounts. Other types of users might want changes to the same report to achieve their own objectives. For example, call centre operators might need a few tweaks to solve client problems faster, or accountants might use it to prepare end-of-year tax returns. After a few such updates, the original value statement no longer captures the purpose for the feature. Teams sometimes try to make the user story at the top more and more generic, to encapsulate all the needs and personas, but then it becomes too vague.

The challenge for this week is: How to write a good description for a feature or a scenario? Think about a feature that evolved over time, potentially through dozens of stories or tasks. What are your ideas on how to structure those descriptions? Should they look like a user stories, or something else? What should they contain?

Post your suggestions using the link below. If you’d like to send a longer comment, perhaps write a blog post and then just send us the URL (you can do that using the same form, link below).

Also, if you have a problem related to Given-When-Then that you’d like help with, or know a topic that might be interesting for the community to discuss, please propose it as a challenge for one of the next articles using the second link.

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the challenge with your friends and team members by clicking on one of the social icons below 👇

The post Should the feature description look like a user story? #GivenWhenThenWithStyle appeared first on SpecFlow - Cucumber for .NET.

Working with BDD Remote Part 2: Drive development with Gherkin

$
0
0

In Part 1 we have discussed how we can ensure shared understanding in remote environments using examples during the online requirement workshops. As mentioned there, those workshops focused on getting the most out of the precious time together, therefore the result (the rules and the examples) have been captured in a simple form, not using Given/When/Then.

Let’s say we had an online discovery meeting where we collected illustrative examples for our user story on a Miro board as virtual sticky notes. Those examples are enough for the participants to start working on the story, but we want more. We would like to have our findings properly documented (so that it is understandable even a few months later) and we would like to verify whether the implementation that we are going to produce is fulfilling those expectations. Such documentation would be also useful for those team members who could not join the requirement workshop.

With BDD, we transform the examples into BDD scenarios using the Given/When/Then keywords and save them to feature files. This transformation, the practice of writing BDD scenarios is often called Formulation. The format of the scenarios and keywords is called Gherkin. The scenarios written with the Gherkin syntax can be executed as automated tests by different tools, like SpecFlow or Cucumber. With the scenarios automated and executed, our documentation can be kept up-to-date all the time, hence it can also be called living documentation.

An example formulated to Gherkin scenario (source: Gaspar Nagy)

There are plenty of good hints and practices on how to write good BDD scenarios and these are also true and important in remote environments. In our book “Rose, Nagy. Formulation – Express examples using Given/When/Then (BDD Books 2)“, we have summarized the most important principles using the BRIEF mnemonic: Business readable, Real data, Intention revealing, Essential, Focused and brief.

The formulation can be done in a distributed way, but doing it by a single person is not ideal, because it is easy to get stuck in a wrong thinking model. In many BDD teams, the scenarios are formulated by a pair of people. Most typically a tester works in pair with a developer, where the tester can focus on the business and testability concerns and the developer can match them to the automation and the system architecture. Finding an hour’s time where the pair can work together on the formulation of the examples is not that difficult, even if we need to adjust our daily routine to other family members we are home officing together with.

For online formulation no special tooling is necessary.  Any editor would be sufficient that you can share with your pair. As the feature files are normally version controlled (e.g. using Git), the pairs can also choose to save their newly formulated scenarios to a source control branch and submit them as a pull request. Using this approach, you can let other team members or business stakeholders get a review of the scenarios and/or formally approve the expectations that the team is going to work on. You remember, don’t you, we wanted to avoid rework caused by the reduced chances of feedback. So it is better to make sure that we work on the right thing.

Good Gherkin scenarios are business readable, and via automation, they verify the application. When they fail, the failure is understandable both by the business side and the delivery side of the project. So essentially they make a connection between the problem and the solution or with other words the members of the distributed team. Scenarios represent our shared knowledge and shared responsibility to produce quality software.

In the next part…

In this post, we have seen how the expected behavior can be efficiently documented using the Gherkin syntax so that it provides exact guidance for remote team members to work in a distributed setup. Formulation, the activity of turning an example to a scenario, can be done the best by a pair of people. The pair should review the business language and the phrases used in the existing scenarios and based on that write the scenario. The scenarios capture the expected behavior in a business readable format but can be processed by BDD tools, like SpecFlow or Cucumber to turn it into automated tests. These tests can drive the development process. 

To get the full benefits of the BDD process, we would like to turn these automated BDD scenarios into living documentation. In remote environments, we are more dependent on shared information that we can access in a distributed way. In Part 3, we are going to give some hints on how to make the living documentation better accessible.


Gaspar Nagy is working as a BDD trainer & coach. Check out his public BDD with SpecFlow, BDD Vitals or BDD with Cucumber courses or request an in-house private course for your team. He is a BDD addict and as such he’s editing a monthly newsletter about interesting articles, videos and news related to BDD, SpecFlow and Cucumber.

The post Working with BDD Remote Part 2: Drive development with Gherkin appeared first on SpecFlow - Cucumber for .NET.

Download the Sample Code for Challenges 1 and 2 #GivenWhenThenWithStyle

$
0
0

If you liked reading about the solutions in the Given-When-Then with Style series, and you would also like to know how those would be implemented in real work, you can now download a wonderful demo project by Zoltan Toth (Developer, Specflow team at Tricentis). This is a great way to explore the challenges further, and see how the feature files mentioned in the solutions would be transformed into automated tests in real life.

Zoltan did an excellent job implementing the solution from the first two challenges as a C#/Specflow project. The project covers the scenarios from How to set up something that’s not supposed to exist and How to structure a long list of examples.

The code demonstrates several common patterns for organising Given-When-Then projects, such as where to store the implementation of steps relative to the system under test, and how to link the feature file with automation code. It also shows a few nice tricks for implementing Given-When-Then steps that include tables, which can significantly simplify specifying lists of objects with multiple properties.

Note that this project is focused on the Given-When-Then patterns and the automation workflow, and it is not trying to demonstrate how to build the business part of the code. The system under test is intentionally trivial, using an in-memory data store and simplified Unicode mapping. This is good for demonstration purposes, so you could focus your attention on the parts that are important for this article series. In real work, the code would evolve from this point to proper Unicode support, most likely using a third-party library for string normalization, and a repository that connects to a persistent storage, such as a database. In a TDD flow, this work would usually be driven by more technical unit tests and integration tests, which would help evolve technical design without really changing the business functionality.

To download the code, head over to GitHub.

The history of the changes on GitHub is also well worth checking out, as the project demonstrates the typical flow of working with executable specifications. Here are some interesting things to notice:

The final feature file specification is slightly different from the one in the article. This is perfectly normal, as small inconsistencies become clear only once we start automating step implementations.

Developers will notice slight differences in wording between similar steps in different scenarios. Small inconsistencies may not be noticeable to people who participated in a specification workshop, since they have the right context from the discussions which led to the examples. However, someone reading the same feature description a few months later might be confused by inconsistencies. These can, and should, be fixed as you automate the tests. In the Specification by Example book, I call this step “Refining the specification”.

More importantly, you’ll see that the last scenario has different examples from the one in the solution article. During test automation, developers sometimes notice that it would be easier or more consistent to use alternative data for the same purpose. By consolidating the example data, the last scenario was also able to use the common background set-up, making it shorter and more focused. Replacing examples with alternatives that better demonstrate the underlying rules is also OK, as long as they actually serve the same purpose. This is why it’s so important to provide good contextual information around each example, such as the response messages or case names. Without that contextual information, people refining the specification might think that an alternative example is equivalent, but it may not be. When in doubt, check with the business representatives or other workshop participants if replacing examples.

Automation costs are often higher at the start. In a usual development flow with Given-When-Then feature files, adding initial examples requires putting in new automation code, new bindings for step implementations, and experimenting with the design of the automation layer. After the first few examples are working, the structure is in place, and adding new scenarios becomes very easy. You can see that the first half of the version control history contains more changes to automation code than the system under test, but the second half is almost all about working on the business part of the system, with very few changes to test code. People new to this kind of test automation are sometimes concerned about the overhead of working with executable specifications. When done right, automated tests don’t slow down the work, they actually help people move faster with confidence. There is some cost of course, usually towards the start, which pays off quickly as the complexity grows.

The development flow was different from how the features evolved during the discussions. Looking back at the solutions for the first two challenges in this series, you can see a typical flow of information discovery. We started with simple examples, provided counter-examples to clarify rules, then identified related questions by identifying boundaries. This sometimes produced more questions and scenarios. The flow of that discussion was significantly different from the flow of the final feature file. Once a feature got clarified nicely, the development flow could proceed quickly. We did not need to identify all those questions and go back and forth while coding.

The development flow was not always Red-Green-Refactor. The typical TDD cycle calls for adding a failing test before any code changes. However, when reviewing a feature file from the top to bottom, if you try to follow red-green-refactor, you may sometimes not really get to the red stage. Some specifications might already be covered by the time you consider them.

A feature file should be designed for readability. Its purpose is to serve as good documentation. It’s normal for follow-up scenarios to clarify preceding ones, to ensure that people who implement the system don’t forget about important concerns. However, when implementing the initial scenarios, people who participated in the specification workshop already know the wider context so they might do the right thing straight away.

On a related note, you do not necessarily have to follow feature file from top to bottom when implementing it. Developers should be able to pick and choose various scenarios from the feature file to evolve the code gradually. Development flow usually follows technical complexity, and feature file flow usually follows business complexity. It’s OK for them to be different. As long as all the scenarios pass at the end, it doesn’t matter really what sequence people implement them in.

Feature files don’t cover everything a developer needs to do. The underlying code sometimes evolved without changes to feature files or having a failing test that needs to turn green. As people start implementing a feature, they might discover additional questions or have ideas on improving the system. In particular, a feature file shouldn’t imply technical aspects of system design, and developers will likely want to add more tests to cover those in a true TDD flow.

To keep things simple and focused on Gherkin and Specflow, this project doesn’t contain any technical unit tests. In real work, if you want to keep the red-green-refactor cycle, it’s normal to add and extend the feature file with business-oriented tests, and add unit tests to drive the technical aspects of design. Testers might also want to add more examples, or to explore the system further by trying out different boundaries. Beware of adding too many additional scenarios or examples to the main feature file, though, as this might make it confusing and overly complex. We’ll deal with this in one of the following challenges.

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the challenge with your friends and team members by clicking on one of the social icons below 👇

The post Download the Sample Code for Challenges 1 and 2 #GivenWhenThenWithStyle appeared first on SpecFlow - Cucumber for .NET.

Solving: Should the feature description look like a user story? #GivenWhenThenWithStyle

$
0
0

The challenge for last week was to provide guidelines for good context descriptions. The contextual descriptions in many books and online articles look similar to user stories. However, story-like descriptions get tricky for features that evolve over time. For a detailed explanation of the problem, check out the original post. This article contains an analysis of the community responses, and tips on more general approaches to solving similar problems.

Faith Peterson wrote two great responses to this challenge. The first looks at a more general issue of solving the right problem, identifying how “trying to write something vague is often an indication that something has gone wrong with my analysis.” She warns against describing solutions before understanding the real problem. Although not directly related to the challenge, I strongly recommend reading this article.

What makes a good feature or scenario description?

The context description, and this applies equally to features and scenarios, is there to provide additional information that is too long to keep in the title, but would be impractical to show in individual examples. The text in the context description should ideally help the readers and maintainers understand examples more easily and make good decisions about updating the data in the future.

Because the contextual descriptions do not get automated, they do not have to follow a rigid structure. You do not have to force yourself to fit it into any specific format. So, to answer the question from the title, related to user stories: it may, but it doesn’t, and often it should not.

User stories are a format for planning work, not for documenting features. Instead of abusing a story format for documentation, here are some tips for writing a good description:

  • Explain the purpose of a feature or scenario
  • Document complex business rules
  • Document important decisions about scope
  • Explain the structure of the examples
  • Avoid repeating the data
  • Predict questions about the examples and answer them

Explain the purpose of a feature or scenario

The opening statement of a feature file should talk about the purpose of that feature. Explain why that whole thing is needed, so that readers can know if they’re viewing the right file or not. This also helps immensely to set the stage for the scenarios.

If a user story fits that purpose, by all means use it. Very often, when adding a new feature or scenario, the initial story is a good way to capture the purpose. As the functionality grows, rewrite the context, clarify it, and throw away the parts that don’t make sense. Some people keep adding multiple story descriptions to the feature description, but this becomes too long and too boring very quickly.

Faith suggested restructuring feature files as the functionality evolves. One of the options in her post is to split up a feature file into a folder of files, each with a very specific purpose. This is a good way of dealing with a feature that grows in different ways, and various aspects of the functionality deal provide value to different people. If the same piece of functionality serves several purposes, then it’s better to rephrase or restructure stories into something else.

When restructuring work-item descriptions (such as stories) into feature descriptions, beware that they often need different levels of specificity. The original intent of user stories was to be a promise for a conversation. It’s OK, and even expected, to leave important details out of stories and iron them out during a discussion. Feature files should document the results of that conversation, not leave a bunch of questions open for further clarification.

When working with a feature that evolved to address the needs of multiple personas, sometimes people try to rephrase the user category into a more general one. Faith gives the example of “Report Consumers” or “Report Readers” which could imply all the various types of employees that access an invoice report, and shows how that’s too broad to help with understanding.

I prefer to list all the related roles, but perhaps in a different way. Usually, you can throw away the “I want” part of the user stories because it will be implied by the scenarios in the file. You can group the “So that” parts under personas, then remove duplicates or consolidate similar entries into a more general description. Instead of listing a dozen story blocks, focus on the big picture:

Feature: Account reporting

   The account reports are primarily used by account managers 
   to control client credit risk. They also use these reports
   to decide on eligibility for discounts and special deals.

   Other users include:
     * call centre operators, who use them to quickly look
       at recent transactions to assist clients when on call
     * accountants to audit end-of year tax reports

Scenario: …

Faith suggests writing a “narrative” instead of a story, and provides a great example identifying a persona, a need and the proposed solution in a much longer format.

Feature: Pending Invoice Report

  Sabine, an account manager, is responsible for maintaing Acme
  Financial's credit risk exposure within defined levels. One way
  she does this is to closely monitor whether customers who have a
  history of slow payment have paid their current invoices or if
  those invoices are at risk of becoming past due. When she does
  this, she wants to see a list of all customers with billing
  dates within the next three business days with invoices that are
  pending. (Pending means the invoice has been issued and payment
  has either not been received or not been settled.)

For very complex features, you can also use this kind of scoping introduction for individual scenarios or scenario outlines. For example, if there are some specific features introduced into the reports for call centre operators to assist clients quickly, list that in the introduction of the related scenarios only.

Document complex business rules

For scenario descriptions, documenting the purpose often means explaining the underlying business rule. Sometimes a single scenario is not enough to demonstrate a business rule, so it’s useful to provide a more general description of the rule for a whole group of scenarios. The feature file description is a good place to do that. Faith provides a nice example in her post:

Feature: Pending Invoice Report

  Rule: Account Managers may only see pending invoices 
       for their assigned customers
  Rule: Account management supervisors may see pending invoices 
       for customer assigned to the account managers they supervise

Note that Gherkin (the tool-independent language for Given-When-Then feature files) introduced a special keyword for grouping scenarios, Rule. Not all tools support it though, but Specflow does. The Rule keyword allows you to provide some context that applies to a set of scenarios, but not necessarily the whole feature file.

Document important decisions about scope

You can use the context descriptions to better manage the expectations of stakeholders or colleagues outside the immediate team. A good title should explain the overall business scope, but it cannot document design scope decisions. There’s just not enough space. If you’ve postponed some functionality in order to deliver an initial version faster, or agreed that something is completely out of scope, list it in the introduction of the file so people won’t bother you about it later. Explain questionable aspects of what’s in scope, what’s out and why. For example:

Feature: Account Reporting
  … 
  This feature explicitly excludes transactions from the legacy
  database, which will be added in a future iteration (Christian
  approved this by email on 13th January). 

This trick applies equally well to scenarios and scenario outlines. If you decided to exclude a class of examples from a scenario, you could in theory set up examples that show how the system rejects them, but that might be an overkill. Sometimes, it’s enough to just explain why they were excluded. For example:

Scenario Outline: Filtering by currency

  European currencies other than EUR are not listed below, since
  it's currently not possible to set up an EU account with a
  different currency through the user portal.  We may need to
  revisit this once the user portal is updated.
  … 

Explain the structure of the examples

If the examples are not self-explanatory, make sure to document the structure.Explain why important pieces of data are listed in scenarios, and why some other equally important fields are not there. Having the structure of the examples explicitly documented helps to focus the attention of the readers, and align everyone on the terminology and the model.

Feature: Account Reporting

  … 
  For risk assesment, we only use the Consolidated Net Amount,
  which is the after-tax value converted to the seller primary
  currency. The reports ignore delivery costs and tax.  These are
  not important for assessing risk but may vary by country, so
  including them could lead to complex calculations and misleading
  results.

Faith goes even further and suggests explicitly listing definitions of important terms in the description:

Feature: Pending Invoice Report

  Account Manager: person who manages Acme's relationship with
                 a specific set of customers
  Pending Invoice: invoice that has been issue but has not yet been paid

For simple feature files, where all the scenarios use the same structure, you can do this in the feature header. For more complicated feature files, it might be worth doing this in individual scenario descriptions.

Avoid repeating the data

A common way to mess up the scenario or feature description is to summarise the data from the examples. This is particularly important to remember if you want to document a business rule in a description.

When the scenario or feature context repeats the data from the examples, then it’s not adding information to the executable part of the specification but just complicating it. This is a common symptom of a problem that causes long-term maintenance headaches. Here is an example:

Scenario: risky accounts should be shown separately

Any client accounts with overall risk factor > 0.5 should be shown
on the 'Risky' tab

Given the following user accounts
| account id    | risk factor |
| low-1         | 0.10        |
| low-2         | 0.49        |
| at-limit-3    | 0.50        |
| above-limit-4 | 0.51        |
| high-risk-5   | 0.99        |
When the client account report is generated
Then the following accounts should appear in the 'Risky' category
| account id    | risk factor |
| at-limit-3    | 0.50        |
| above-limit-4 | 0.51        |
| high-risk-5   | 0.99        |

The issue with this scenario context is that it just gives us the same information again, in a different format. It does not provide any further information to understand the purpose or structure. Even worse, because the context description is not tied to an executable step, it is not easy to keep up-to-date with the rest of the system. If someone decides to change the limit in the future, they might update the examples but forget to update the context. This is a common cause of confusion when maintaining feature files.

It would be much better to make the context more generic and explain the purpose, so people can decide better in the future how to edit the examples. In addition, making the context more generic often forces people to think a bit harder about the model. What does this magical value 0.5 actually represent? This is another situation when the technique give it a good domain name helps. Identifying a good name, such as ‘account risk threshold’, makes it clear that this is a property of the model and allows us to design a system so it’s easier to change in the future. Here is one potential way of rewriting this scenario:

Scenario: risky accounts should be shown separately

  Client relationship managers need a quick way of identifying
  accounts that should not be allowed to get discounts, long
  payment terms or large credit. 

  We currently do not have specific risk factors for those three
  categories, but use the overall risk factor on the account (this
  is likely to change in the future). 

  The Risky tab of the account reports should list all accounts
  where the overall risk exceeds the account risk threshold, but
  only those accounts.

Given the risk threshold of 0.5
And the following user accounts
| account id    | risk factor |
| low-1         | 0.10        |
| low-2         | 0.49        |
| at-limit-3    | 0.50        |
| above-limit-4 | 0.51        |
| high-risk-5   | 0.99        |
When the client account report is generated
Then the following accounts should appear in the 'Risky' category
| account id    | risk factor |
| at-limit-3    | 0.50        |
| above-limit-4 | 0.51        |
| high-risk-5   | 0.99        |

If the risk threshold changes in the future, we don’t need to update or change any features or tests. By identifying it as a domain property, we can make the system configurable. Also, if the underlying logic needs to change, it’s easy to identify features that include the risk threshold and discuss how they should be updated. The scenario context will help us decide how to split it and re-write it. A generic “As a … In order to … I want …” statement would not come even close to that.

Predict questions about the examples and answer them

Of course, there are many more things you could add into the feature description. One of the best ways to describe the feature or scenario context is to check what kind of higher-level information is missing from the examples. After all, the free-form text is there to complement the executable examples and make them easy to understand. Show the feature file or a scenario to someone who did not participate in the specification workshop (discovery session, or collaborative analysis session). Find someone who understands the domain, but was not part of the discussions. Just show them the feature file, ask if they understand it, and keep quiet. See what kind of questions they ask, and try to answer them all in the context description. It’s very likely that someone reading the same file in the future might have similar questions, and having the answers ready will mean that they don’t have to chase you about it.

You can test if you’ve done this correctly by showing it so yet another person, and see if anything else is missing.

Use Markdown with Specflow

As an additional tip, some documentation tools can apply rich-text formatting to scenario and feature descriptions. For example, Specflow allows you to use Markdown to add links, embed images and format text better. When viewing the feature file with LivingDoc, the markdown syntax becomes HTML, allowing you to provide a lot more than just text with your living documentation. You could add web links to related features, or diagrams that explain the scope of a feature better.

Check back tomorrow for the next challenge in this series. Meanwhile, if you have an interesting topic that you would like the community to discuss in one of the following challenges, please tell us about it by using the link below:

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the challenge with your friends and team members by clicking on one of the social icons below 👇

The post Solving: Should the feature description look like a user story? #GivenWhenThenWithStyle appeared first on SpecFlow - Cucumber for .NET.

How to fix a chain of dependent scenarios? #GivenWhenThenWithStyle

$
0
0

This week’s challenge is solving a problem I’ve often seen when manual tests get rewritten as Given-When-Then:

We have a long feature file. The scenarios build on each other, so the result of one scenario is effectively the starting point for another. This made it easy to write tests, but it’s making it difficult to maintain them. Someone updating a single scenario at the top of the file can break a lot of tests later in the file. How would you fix this?

When working with a system that has limited automation support, or performing actions that are very slow, it’s quite common (but unfortunate) to specify a feature through a chain of scenarios. Each scenario sets up the stage for the next one, expecting the context to continue from the preceding test executions. Some sections will even imply the “Given” part. For example:

Scenario: (1) register unique user

Given no users are already registered
When a user registers with username "mike99"
Then the registration succeeds

Scenario: (2) create order requires payment method

Given the user adds "Stories that stick" to the shopping cart
When the user checks out
Then the order is "pending" with message "Payment method required"

Scenario: (3) register payment method

Given user adds a payment method "Visa" with card number "4242424242424242"
When the user checks out the orders page
Then the order is "pending" with message "Processing payment"

Scenario: (4) check out with existing payment method

Given the user adds "Stories that stick" to the shopping cart
When the user checks out
Then the order is "pending" with message "Processing payment"

Scenario: (5) reject duplicated username

When a user registers with username "mike99"
Then the registration fails with "Duplicated username"

There are three downsides of this type of structure.

The first is that the readers have to remember the context. For example, scenarios 2 and 4 have the same precondition and trigger, but a different outcome. In this particular case, as there is only one scenario between them, it’s relatively easy to guess why they are different. As the list grows, that becomes significantly more difficult.

The second is that updating scenarios becomes quite tricky. If, for some reason, we wanted to change the username in the first scenario, the last scenario also needs to change. Otherwise, the test based on that scenario will start to fail mysteriously.

The third is that problems in executing tests usually propagate down the chain. If a test based on the third scenario fails because of a bug, the fourth scenario test will likely fail as well, since the context is no longer correct. This can lead to misleading test reports and complicated troubleshooting. Verifying a fix also becomes more difficult, since it’s not possible to just execute the third scenario in isolation – we have to run the first two as well.

To fix the downsides, we also need to consider the reasons why people keep writing such specifications. Because each scenario extends the previous one, it’s quite easy to write the initial version of such a file. Also, as the steps from the previous scenarios do not need to be repeated to set up the context, the overall execution is quicker than if each scenario were to set up everything it needs from scratch.

Post your suggestions using the link below. If you’d like to send a longer comment, perhaps write a blog post and then just send us the URL (you can do that using the same form, link below).

You can submit your solution by Monday, July 13th.

Stay up to date with all the tips and tricks and follow SpecFlow on Twitter or LinkedIn.

PS: … and don’t forget to share the challenge with your friends and team members by clicking on one of the social icons below 👇

The post How to fix a chain of dependent scenarios? #GivenWhenThenWithStyle appeared first on SpecFlow - Cucumber for .NET.

Viewing all 197 articles
Browse latest View live


Latest Images