SpecFlow & Gherkin Testing Guide
SpecFlow & Gherkin Testing Guide
Module Overview
Hi I'm Jason Roberts from Pluralsight, welcome to this course on Automated Acceptance Testing with
SpecFlow and Gherkin. In this module we'll get an overview of what SpecFlow and Gherkin are and
how we can use them to define acceptance tests that we can automate. In this module we'll get an
overview of the SpecFlow tool itself. We'll discuss acceptance tests in general and some of their
characteristics. We'll learn how we can broadly categorize testing into business facing and technology
facing tests. We'll see how SpecFlow can be used in test-first approaches. And we'll look at the
structure of SpecFlow tests and we'll breakdown the different components. We'll look at how we can
install the Visual Studio IDE integration for the SpecFlow tooling. And finally we'll create a new project in
What is SpecFlow?
So what is SpecFlow? SpecFlow is an open-source tool for bridging the communication gap between
business analysts, to write acceptance tests for the system that we're developing. SpecFlow enables
these acceptance tests to then be automated and allows them to be executed against the system we're
building to verify that the acceptance tests pass. The project site for SpecFlow is at www. specflow. org.
What is an acceptance test? An acceptance test enables us to validate that we're building the right
system and that the system contains the right features that are required by the users, the business, or
the product owner. Acceptance tests are usually from the point of view of the business or the user or the
customer point of view, but we may have a proxy for these people in the role of the product
owner. Because acceptance tests are from the point of view of the business, they should be written in a
non-technical format. This format can have some formatting rules to enable it to be translated
into automatable steps, but it should be an easy to understand and use format. Acceptance tests, when
executed, are a pass or fail affair there's no middle ground. The acceptance tests help to document
what the system should do, they may compliment other documentation or they may form the sole
specification of what the system should do. It depends on the team and project. When acceptance
tests are automated they can become what's described as living documentation. For example, if the
system is documented in a Word document or Excel spreadsheet it's easy for them to become out of
sync with the actual system and how it behaves. By automating acceptance tests we create a link
between the specification and the actual implementation. If the acceptance tests are changed or the
system is changed in an unexpected way, then we can execute our documentation, the acceptance
tests, to verify that things still work as expected. And acceptance tests, when executed, usually exercise
a vertical slice through the system. So in contrast to unit testing where we're just concerned with testing
a single class or integration testing where we might be testing groups of classes and components,
acceptance tests can touch all of the different layers in a system. From the lower data layers right up
through the business layer and even to the UI layer. If the acceptance tests are created collaboratively
and then used to drive what's actually being built, it can enable the whole team to have a
shared understanding of exactly what's required of the system. Acceptance tests can help to define
what done means so the scenarios that make up a feature, once they all pass we can say that the
Conceptually one way of grouping tests is to divide them between those tests that face the business
and those tests which face the technology. So acceptance tests can be thought of as business facing
tests in that they're designed to be written and read by business people. So this could include product
owners, users, and business analysts, but it's also a good idea to get the whole team involved
when creating acceptance tests and this includes the testers and developers. Technology facing tests
on the other hand are generally only created by developers and these tests test the
technology implementation. So here we're talking about things like unit and integration tests and
processes such as test driven development. Martin Fowler defines business facing tests as "A test
development team such as customers, users, business analysts, and the like". Whereas our technology
facing tests are really written by and for developers. It would be extremely hard for non-programming
process, conceptually it could even be used in a waterfall scenario, but this is certainly not what it's
aimed at. And also hybrids of all these approaches. SpecFlow itself doesn't impose any specific
When SpecFlow is used in test-first approaches we first start off by discussing with the team what the
system should do next. And it's important at this point to get the business or users or product owners
involved in the conversation. Once we've had this conversation we can then write the tests in
SpecFlow. Once we've written the test we then start to write our code. Once we've written our code we
execute the SpecFlow tests to see if they pass. If the tests don't pass we continue writing code until
they do. Once the tests pass we can then define what the system should do next. And go around the
circle again.
SpecFlow Structure
So let's take a look at the SpecFlow structure itself. First off we have our Visual Studio integration and
we'll see in just a moment how we can install this. So with SpecFlow we start at the top with a feature
file. So a feature could be something like the addition feature in a calculator. Once we have our feature
file we define a number of scenarios that describe individual usages within this feature. So for calculator
this could be a scenario where two positive numbers were added and another scenario could be
where two negative numbers were added. Logically a scenario consists of a number of steps and in
general terms these describe the state before, the action during, and the expected state afterwards.
Sometimes in unit testing this is described as a range act assert, but when we come to discuss the
Gherkin syntax, in the next module, we'll discuss this further. For each individual step in the scenario we
can write the actual test code that interacts with the system under test. And to create this test code we
make use of a testing framework. So this could be something such as NUnit. The testing framework
such as NUnit can integrate with our test runner in Visual Studio and allow us to make use of the testing
frameworks assert methods to determine ultimately whether a scenario has passed or failed. The
SpecFlow Visual Studio integration, amongst other things, allows us to run tests right from the scenario
level without having to locate the individual test code and execute the tests from that level. The feature
file and its scenarios are written in a business readable language and again we'll cover the Gherkin
language in module 2.
So let's see now how we can install the SpecFlow Visual Studio integration. We'll then go and create
our first feature file. We can install the SpecFlow integration by opening the Extensions and Updates
window and searching on line for SpecFlow. So here we're going to install the SpecFlow extension and
hit Install and I'll just close this window. And we just need to restart Visual Studio for the install
to complete. So let's go ahead and do that now. So the first thing we need to do is create a new class
library project in this empty solution. So let's go ahead and do this we'll add a new project and we've
got Class Library project and we'll go ahead and call this Foo. specs. So let's go ahead and click OK.
And we'll just delete the auto added class 1 from the solution. So now we have our class library
project to hold our features, we can go and add our first feature file. So we'll go to Add New Item and
notice now that we've got these new SpecFlow items that we can add. So we'll start here by selecting
SpecFlow Feature File and we'll just give it a name AwesomeFeature and we'll just hit Add. So when
we add a feature we get a template that we can use to start writing our feature and this is written in the
Explorer we've got our Feature file now and inside this Feature file we can define a number of
Scenarios to create our acceptance criteria for this feature. Once we've created our Scenarios each
of these steps within the Scenario we can automate and verify that the system behaves as expected.
Module Summary
So that brings us to the end of module 1. In this module we gained an overview of SpecFlow itself. We
learned what acceptance tests are and some of their benefits. We saw how we can group tests into
business facing and technology facing tests. And how we can use SpecFlow in test-first approaches.
We learned about the basic structure of SpecFlow tests and we saw how we can install the
Visual Studio extension for SpecFlow. And how to create a new feature file. Join me in module 2 when
Module Overview
Hi welcome back to module 2. In this module we'll be looking more closely at the Gherkin language that
we use to write our acceptance tests. In this module we'll be learning how we define features
and scenarios within those features. How we use the given, when, then steps to flush out our individual
scenarios. We'll look at how we can classify scenarios and features by using tags. And how we
can comment out individual lines within our features. We'll look how we can define tabular data to be
passed to the automation for our steps. And how we can use scenario outlines to create data driven
scenarios. Finally we'll look at backgrounds and how we can use them to provide common setup for all
of the scenarios in a given feature file. So let's get started by diving into features.
What is Gherkin?
natural language that the business can understand as opposed to code that only programmers
can understand. It's a line-oriented language where new lines represent distinct elements and it uses
indentation to create a sense of structure. Gherkin has a number of keywords such as feature and
scenario. And these keywords are localized in over 40 spoken languages, for example
French, Bulgarian, and Japanese, this means that the Gherkin tests don't have to be written in English.
Features
Features are the top level grouping in Gherkin. A feature contains one or more scenarios and we'll look
at scenarios in just a moment. Features group or contain logically related test scenarios. Each feature
file represents a small discreet feature of the system. You wouldn't, for example, create a single feature
file that has scenarios for everything the system did. Feature files start with the keyword feature. This is
followed by a feature name or a short terse description of the feature. Following the feature name we
can optionally add our own free text description to help describe what the feature covers and the
start with the feature keyword and in this case the name of the feature is Brush Teeth. And here we
followed this with an optional text description that we create by indenting, either using spaces or tabs,
a number of lines representing the description. So here the text Brushing teeth is good because it helps
keep them clean, doesn't have anything to do with automation, it's purely some additional information
that we can give to the reader. So our feature free text description can pretty much follow any format or
no format that we desire, but there's a number of standard formats that are often used. The first follows
the format as a, I want, so that. So we could rewrite the free text description in our Brush Teeth feature
using this format. So first off we start with the role or the person and this is the As a human. Next
we have the I want part, so here we're saying I want to brush my teeth. And finally the outcome or value
that we desire, here we're saying So that they stay pain free. And this format puts the role or the person
first. So we can describe this type of description as role or person centric. Some people have criticized
this format in that the value that we're trying to get doesn't appear until the end. So an alternative to this
format is the in order to, as a, I want format. So we can rewrite the previous as In order to stay pain
free As a human I want to brush my teeth. And in this format we put the value in the first line. So we can
describe this as value centric. Again you don't have to use these formats, but they can help to solidify
exactly what the feature is covering. And again you don't have to, you can use any kind of text that you
feel is useful.
Scenarios
So once we've created our feature file we then start to add scenarios to it. Scenarios are concrete
examples of the expected behavior of the system we're testing. Each scenario describes a particular
situation and use of the feature. Each scenario should be independent and isolated from the
other scenarios. So this means that we should be able to run any given scenario at any time and it
should always produce the same result, pass or fail. For example, we shouldn't depend on another
scenario having just been run for the next scenario to pass. Scenarios can represent the happy paths of
the feature so these are the normal expected usages where everything is okay. Scenarios can also
represent what happens when certain error conditions are met. And we can also have our
scenarios represent those edge cases of things that don't occur very often but things that are important
to us. We mark the start of a scenario by using the scenario keyword. We then follow this by the title of
represented like this, the scenario keyword with a code on followed by our title.
of three overall phases. In the first phase we set up the initial state of the system. This is where we get it
ready to perform the specific actions that we want to test. Next we actually perform those actions
against the system. Once we've performed one or more actions we check that the end state of
the system, after we've performed these actions, is as expected. In Gherkin these phases map to the
following keywords. In the setup phase we use the given keyword to create our setup steps. When we
perform actions we use the when keyword in our action steps and for our end state we use the then
keyword. So if we look at this successful brushing scenario we can add our steps to it. So first off in our
setup phase we have our givens and notice these are indented. So here we're saying in our setup
phase we're saying that there's toothpaste on the brush and that the mouth is open, given that these
things are true we next come to our action steps. So here we use the when keyword. When the
back teeth are brushed, when the front teeth are brushed, then the teeth look clean and then the mouth
feels fresh and then the braces aren't damaged. So the thens form our third phase where we check that
the resulting state of the system is as expected. So in this scenario we've got multiple givens, whens,
and thens, but notice that it doesn't really read particularly naturally, so we can try and improve this
scenario by changing it and adding the and keyword. So now our setup phase, the given, reads a bit
more naturally because we've used the and keyword. Given there is toothpaste on the brush and the
mouth is open. We can also use the and keyword in our second phase. So here we're saying when the
back teeth are brushed and the front teeth are brushed, again this reads more naturally than having two
when lines. Finally we can use the and keyword and also the but keyword to make our third phase, the
then phase, read more naturally. We're now saying then the teeth look clean and the mouth feels fresh,
but the braces aren't damaged. So here we've still got our three conceptual phases, but we're making
use of the and and but keywords to improve the readability. And we can go one step further and use
Tags
The Gherkin language gives us the idea of tags. They allow us to mark our features and scenarios with
arbitrary tags. Tags can map to categories in the unit test framework we're using to perform
the automation and allow us to execute only those scenarios that have a specified tag. Tags can be
applied at the feature level or on individual scenarios. If we apply a tag to the feature each of the
scenarios within that feature inherit that tag. Scenarios and features can have multiple tags and we
specify a tag by prefixing it with the @ symbol. The ignore tag is a special case in that it enables us
to temporarily remove tests from being executed. So to mark this successful brushing scenario with the
smoke test tag we simply proceed it with @smoketest. There's a number of potential usages for tags.
We can use them to group features into supersets of features, we could mark certain tests as being
more important than others, we could differentiate between slow running and fast running tests,
we could mark certain tests as being works in progress, or we could use tags to indicate how often tests
should be executed. For example, weekly, daily, or hourly and we could also mark tests for example
So let's have a look at tags in action. So here we've just got a Feature file and we've got three
Scenarios. In our Visual Studio Test Explorer we're currently in the Class view. And here we can see
our three scenarios underneath our Feature file. So let's go ahead and switch to this Traits view and
here we can see our three Scenarios and they have no traits applied. When we talk about traits we're
talking about a similar concept to categories in unit tests. So we can start to categorize these scenarios
by using tags. So let's start with Scenario A and let's say we want to add this to the important category.
If we build now we can see that we've got our Scenario A now belonging to the important Category and
our scenario B and C as not belonging to any category. So let's go ahead and mark Scenario B
as being a fast executing test. And Scenario C as being a slow executing test. Again we rebuild and we
can see now our tests in their different categories. We can add multiple tags to a scenario, so Scenario
A is now important and fast. If we build we can see now that Scenario A is both part of the fast category
and the important category. We can apply tags to the feature itself so let's mark the entire feature as a
work in progress and build. We can now see that all of the scenarios in this feature have inherited the
work in progress tag. So now our Scenario A has three tags applied. These two at its scenario level and
this one inherited from the feature. And we can see this here. Scenario A belongs to the fast, important,
If we need to we can add comment lines by prefixing the line with the hash symbol. So for example let's
just comment out the important tag by applying a hash and notice that it goes green. And say
for example that we wanted to comment out the When line here and just replace it with another one. If
we wanted to we could also comment out to the tag at the feature level or just add arbitrary comments
We can use data tables in individual scenario steps. Data tables allow us to pass tabular data to the
underlying automation step. And they're useful for specifying larger datasets rather than having multiple
individual steps to specify the data. So this scenario here Given I have paste and brush and water when
the back teeth are brushed then the teeth look clean, we could rewrite this using a data table to specify
the given tools we need. So here we've replaced the multiple given steps with a data table we're saying
given I have the following tools paste, brush, and water, when the back teeth are brushed then the teeth
look clean. So to use a data table in a step we add a code on to the end of the step and then we create
a table in text by using the vertical pipe symbol. So here we've created a table with a single column
and three data rows, paste, brush, water with a column name of tool name. When we come to write the
automation step for this given we get a parameter passed to the automation step containing this
data table. We can then use the items in this data table in our automation.
Data-Driven Scenario Outlines
Whereas data tables allow us to pass tabular data to an individual scenario step. Scenario outlines
allow us to execute the same scenario over and over again each time using a different set of test data.
So rather than writing many scenarios with only small variations in data we can create one scenario
outline and then specify the data to be input to this scenario outline when each execution occurs. To
define a scenario outline we use the scenario outline keyword with a colon followed by the name. So
here we're creating a scenario outline called teeth whiteness. When we create a scenario outline we still
use our given, when, and thens and optionally our ands and buts, but this time we can define place
holders for the data that we want to execute the steps with. So here we're saying given I'm using some
kind of brand of toothpaste, when I brush for so many minutes, then the teeth look so much percent
white. So we're not specifying actual values, we're specifying place holders inside these angle brackets.
Now we've created our scenario outline we need to be able to specify the data to be substituted
into these place holders. And we do this by using the examples keyword. Similar to data tables, we
create a table using vertical pipes, but this time our column headers map to place holders in our
steps. Each individual row in the table represents a single execution of our scenario outline. So here
we've got three data rows which means this scenario will get executed three times. The first time with a
brand of Brand X, mins of 1, and percent of 80. The second time with Brand Y and its associated data
and the third time with Brand Z and its associated data.
Backgrounds
Gherkin provides us with the concept of backgrounds. A background provides context to the scenarios
within a given feature. The steps within a background are executed before each and every scenario
runs. To create a background we use the background keyword, followed by colon, and then
the individual steps that make up the background. So here our background states that given I have
teeth and I have some toothpaste. The two steps within this background get executed before
the successful brushing scenario executes and before the toothpaste runs out scenario executes and it
would also execute before other scenarios if we added them. So using backgrounds we can reduce
some duplication in the individual scenarios within a feature. We just have to be careful when we're
using backgrounds that the background doesn't get too large and unreadable and that the things in
Module Summary
So that brings us to the end of module 2. In this module we learned about Gherkin features and
scenarios and how to write scenarios using given, when, and then steps. We also saw how the but and
and steps can help us create more naturally readable scenarios. We saw how we can categorize
features and scenarios using arbitrary tags and how we can add comments to our feature files. We saw
how we can define data tables to be passed to the automation for individual steps. And how we can use
scenario outlines to create data driven scenarios. Finally we looked at backgrounds and how we can
use them to provide common setup steps for each of the scenarios in a given feature file. Join me in
module 3 when we'll see how we can hookup all of our steps in our scenarios to actual code
automation.
Module Overview
Hi welcome back to module 3. In this module we'll be learning how the steps in a scenario bind to the
C# methods where we can actually perform our automation. In this module we'll learn about the
relevant NuGet packages that we need to install to utilize NUnits in SpecFlow. We'll look at two different
styles of binding step definitions to scenario steps and we'll see how we can set a default step definition
style when we generate our step definition files. We'll see what happens when we add a new step and
when we delete an existing step. We'll see how we can execute scenarios and debug individual step
definitions. We'll see how we can share step definitions between scenarios and how we can use
our code. And we'll look at how scenario outline steps map to step definitions. Finally we'll look at how
we share scenario context and data between the individual steps that make up a scenario.
So the first thing we need to do is install the relevant NuGet packages. So we've already got the
SpecFlow extension installed and enabled. And now we have to add in the NuGet packages that we
need. So if we just do a search for SpecFlow we can see that we get a number of results. So if we look
at this SpecFlow NUnit package we can see that this depends on the SpecFlow package and the NUnit
package. So by installing this one SpecFlow. NUnit package we'll get everything we need to start writing
our automation. If we look at our installed packages we can see now that we've got NUnit and this is
the testing framework we'll be using in our automation. We then have the SpecFlow NUnit and the
SpecFlow itself package. So all of these packages combined with the extension that we've already
Now we have our NuGet packages installed and we've written our first Feature file with our first
Scenario. We now need to create the automation for each of the steps within the Scenario. So notice
here that each of these steps is highlighted in purple and this indicates that there's currently no
automation set up for these steps. When we're working with SpecFlow and Gherkin we need a way to
there's a concept of binding. Binding is essentially the hooking up of individual steps to individual pieces
of automation code. And we can see in the Test Explorer here that we don't currently have any tests in
our solution. If we take a look at our Solution Explorer we can see we have our. feature file here and if
we expand this down we can see we have this generated C# file. If we have a look at this we can see
that it's designer generated code and ordinarily we wouldn't change any of this. This code
essentially hooks up our natural language sentences to the SpecFlow framework and the
underlying unit testing framework that we're using. So in this case it's NUnit. And we can see NUnit in
use in this generated code through these attributes, but we don't need to worry too much about
this generated code. So now we need to create a class that contains methods that map to each of
these scenario steps. To do this we right-click and we head down to Generate Step Definitions. When
we open this dialog we're given the choice to select which steps we want to generate step definitions
for. So we're just going to leave this as all of them ticked, we get to choose the class name that we
want to generate, and we have a number of options for the style of binding. So by default it's this
Regular expressions in attributes style. And we'll come back to this in just a moment. For now I'm
just going to click Generate and I'm just going to leave the file name the same. If we have a look in our
Solution Explorer now we can see we've got our new class file. If we have a look at this we can see
that it contains methods for each of our steps in our scenario. So the first
open, The mouth is open and notice here that this is mapped to the conceptual Given phase. We don't
have a separate and attribute and the same is true for the but attribute. The braces aren't damaged
appears as a Then attribute and also notice now that in our Feature file the steps have changed from
purple to white. And this indicates that there's a binding between the natural language and some code.
notice that this step has reverted back to purple. And that's because no binding is available for this step.
So let's go ahead and remove this step definitions file and everything goes back to purple and we'll
right-click again, choose Generate Step Definitions, but this time we'll choose from the style dropdown
the Method name underscores option. If we click Generate now just OK the file name head back to
Solution Explorer and to our step definitions and this time we can see a different style here. The
attributes are now simply Given, When, or Then and the method name has been generated using
underscores between each word. Let's go back to the original style, (Typing) so this is the default
Regular expressions in attributes style (Typing) and notice here that we've got our text specified in the
attribute itself as well as in the method name and again in the actual original scenario step. So say
we change one of these steps, let's change the When the back teeth are brushed step and we'll change
this to read When all the back teeth are brushed. And immediately this changes to purple to indicate
that there's no matching step definition that can be bound to. So to restore this binding we head back to
our step definitions file and we can just change the attribute value here When all the back teeth are
brushed, Save this. And head back to our Feature file. Notice now that this step has gone from purple
back to white. Because the binding between this scenario step and the step definition has been
restored. And in this case it's because we're using the regular expression style, but notice here the
method name we haven't changed. And that's because it's the text in here that creates the binding. We
could actually name this method anything. So when we're working with the regular expression style it's
possible for the actual method name to quickly become out of sync with the actual binding and original
step text. And of course we could change this manually, but this can create some maintenance
overhead. So let's see what happens in this scenario if we use the underscore style. So I'll just
close this and we'll go and delete it and we'll regenerate these step definitions and this time we'll choose
the underscore style as before and hit Generate. And hit Save. So this time we don't have any text in
our attributes and the method names include underscores. And if we go back to our Feature file
everything is white which means we have matching step definitions for each of these steps. So let's
change this back to just when the back teeth are brushed. Again we notice it goes purple and to fix this
we head back but rather than having to change a string into the attribute and also the method name.
We can just change the method name now. So here we can remove the all, we Save this, head back to
our Feature file. And now the mapping has been restored and the step has changed back to white. So
we'll be using the method name underscore style for the rest of this course.
We can specify the default style of binding by changing the app config. So by default when we hit
we've added this trace element and we've set the step definition skeleton style to use method name
underscores. And if you want to know more about this configuration head to this link here. And this will
take you to the SpecFlow documentation. So now we've specified our Method name underscore to be
the default format, if we hit Save and we just get a warning here and SpecFlow's offering to regenerate
our feature files if required, so let's just hit Yes for now. And let's head back to our Feature and now
when we choose Generate Step Definitions we can see that the style now defaults to Method
a third option to use Method name Pascal case instead of underscores, but again we're going to stick to
Let's take a look at what happens now when we add a new step to our scenario. So here we've got our
scenario and we've got our step definitions. So let's go and change this and let's just add an extra And
to the Given section. So it goes purple, so it means we need a step definition for this scenario step. So if
we go and right-click and choose Generate Step Definitions as before we only get the choice of one
because this is the only one that's not bound to a step definition. If we click Generate and we'll keep this
as BrushingOfTeethSteps. cs we get a warning that we're overriding it and if we click Yes and just hit
Yes because this is open in Visual Studio. If we go and have a look at our steps file now we can see
that we've only got one step definition. And if we look in the Feature all of the other lines have turned
purple. And that's because we've overridden the previous step definitions file and now it only
contains this step. So how do we add a new step definition? Well let's reset this, we'll delete this file,
we'll comment out this extra line for a moment and we'll go and generate everything again. Hit Save.
And now we can see our original step definitions. So let's uncomment this line. So we need to create an
individual step definition for this line. And we'll add it to our existing step definitions. So to do this we
head back to our Scenario, go to Generate Step Definitions, again we only get a single choice, but this
time instead of choosing Generate we'll choose Copy methods to clipboard. Now if we go back to our
step definitions we can paste what we've just copied to the clipboard. So here we can see our x_y_z
line in our step definitions now. And if we head back we can see that once we save this step definitions
file our line goes back to white. So that's how we can add a new step definition when we add a new
Deleting Steps
If we want to completely remove a scenario step we simply delete it from the scenario, so we've just
removed the x_y_z step and we can just hit Save and that's all we really have to do. However,
there's also the matching step definition and if we leave this here it won't cause any problems, but we
have to be careful that we don't create a maintenance problem in the future. So assuming this
step definition isn't shared with multiple scenarios, and we'll have a look at this concept later in the
module, we can simply go ahead and delete the step definition. One small tip is that when we're
deleting or editing a scenario step if we're working with a lot of scenarios and features, we can use a
delete And the front teeth are brushed, we could first right-click and choose Go To Step Definition. This
will then open the step definition file in the right place. So we can just delete the step definition, head
So once we've got our scenarios and our step definitions how do we actually execute our scenarios and
kickoff the test automation? The first thing to check is that if you haven't already got it installed in
Extensions and Updates you need to check that you've got the NUnit test adaptor for Visual Studio
installed. Because we're using NUnit as our underlying framework and we're using the Visual Studio
Test Explorer we need to install that extension in order for Visual Studios Test Explorer to understand
NUnit. So to run a scenario we can simply right-click on it and choose either Run SpecFlow Scenarios
or Debug SpecFlow Scenarios. Let's hit Run. If we have a look at our test output for the
successful brushing scenario we can see that we get a message telling us that one or more
step definitions are not implemented yet, but we have actually generated our step definitions here. The
reason we're getting this message is because when we generate the step definitions the generation
adds the ScenarioContext. Current. Pending line. And this instructs SpecFlow that although we've
generated our step definitions we haven't actually implemented any code in them just yet. If we needed
to debug one of these step definitions we simply place a breakpoint in the step definition and we
can right-click and choose Debug SpecFlow Scenarios instead of Run Scenarios. So let's click this and
definition. In addition to right-clicking on the scenario and running it from there we can also use the
Test Explorer to search for specific scenarios to execute. So here we've just got one scenario, but we
could run it by right-clicking and hitting Run or Debug Selected Tests. So that's how we can execute a
scenario by either right-clicking inside the Feature file or by using the Visual Studio Test Explorer to
Our step definitions can be shared between multiple scenarios. So here we've got our successful
brushing scenario and all of the steps have matching step definitions. So let's go and add another
scenario. So notice here that the When x and then z lines are showing as purple because they don't
have any matching step definitions, but this given line There is toothpaste on the brush is showing as
white. Even though for this new scenario we haven't generated any steps. And that's because
there's already a step definition for this Given step. So if we right-click and choose Go To Step
Definition we can see that there is in fact a step definition for this line. If we navigate to the Given in
the first scenario we can see that it goes to the same place. So this Given step definition currently will
execute for both this scenario step and this scenario step. This sharing of matching step
definitions means that we can make use of code reuse in our automation steps, but this does, however,
mean that when we're changing step definitions we just have to be aware that if they're shared they will
say we had this scenario here and we want to modify it to specify the amount of toothpaste on
the brush. So let's say here Given there is 1 gram of toothpaste on the brush, so we need to go and
this and just double-check that it's gone white. And let's say now that we also want another scenario
with 2 grams of toothpaste. So let's take a copy of this and let's just call it Successful brushing 2. And
we'll change this to 2 grams of toothpaste on the brush. So we can go and copy this step definition and
change this to 2 grams. Again we'll Save it and just double-check here that everything's okay, which it
is. So now we've got two scenarios where the only difference is in this Given line. So although all of
these step definitions will be reused, we've got two step definitions for these Givens that only differ in
the data, ie the number of grams of toothpaste that's on the brush. So here in our step definitions we've
got two steps that when we come to write the actual automation will be extremely similar. So we can
reduce this code duplication in our step definitions by making use of parameterization in our
step definitions. So let's go and delete these two Given step definitions. Hit Save and head back and
notice now that we've got our two Givens that are not bound. If we right-click and go to Generate
Step Definitions we can get a preview, but notice that in this preview it's only going to generate a single
Given for the two selected Givens that we've specified here. So let's just close this preview and Copy
the methods to the clipboard and head back to our step definitions and let's just paste this in. So we've
only pasted in one Given, if we Save it and head back to the Feature notice now that both of our Givens
have turned white. Also the number in these Givens has changed to an italicized grayed out color. So
why is this? Well if we have a look at the generated step definition we can see now that in place of the
actual number of grams we have this P0, we also have a parameter that's now been added to our step
definition, also called p0. So let's go and put a breakpoint here and let's debug our first successful
brushing scenario. And our breakpoint gets hit and if we have a look at the value of this parameter we
can see that it's a 1 representing the 1 gram from our scenario step. If we stop this and instead
debug successful brushing 2 once our breakpoint gets hit we can have a look at our parameter value
and this time we've got the number 2 to represent 2 grams from the second scenario. So in this way
to we can go and rename this parameter so let's call it grams of toothpaste, or just grams. If we debug
this now we can see that we get our value for grams of toothpaste and we can also improve the
readability of the step by replacing this P0 with something more meaningful. To do this we just
specify something in capital letters. So let's just call it GRAMS. Again if we debug this we can see that
we get our grams of toothpaste. If we go and change the values in the Feature, so let's say 11 and
22, and just choose one of these to debug notice now that we get our new value for grams. So that's
how we can use parameterized step definitions to reduce the code duplication in our step definitions
where we have the same basic step definition but it only varies by data values.
We can also pass multiple parameters in a single step definition and also specify both strings and
numbers. So let's change this Given and we'll add two parameters. So again we'll add so many grams
of toothpaste and we'll also add another parameter to specify the brand of toothpaste. So let's just make
up a brand called Brand X. So Given there is 2 grams of Brand X toothpaste on the brush. So we can
go and edit our step definition now. So Given_there_is, (Typing) and again we use upper case to
now have to add the method parameters. So the first one is a number, so we'll say int grams to match
the uppercase grams here and also we'll add a string parameter called brand to match the brand
parameter here. And these parameters in the method definition have to be in the same order as they
appear in the method name. Hence we've got GRAMS and grams first and BRAND and brand second.
If we Save this and head back to our Feature we now see that our Given step has gone back to white
and now we've got two parameters signified by the italics and the gray 2 and Brand X. So let's debug
this and we'll see what parameters we get passed. So let's go across here to our parameters and
for grams we can see we've got 2 and for brand we've got Brand X. We can go here and change this to
say 22 and Brand Y, debug the test again check our parameters and we have 22 grams and Brand Y.
We saw in the previous module how we can specify data tables in Gherkin. So here in this Given step
we've specified a data table containing two columns, ToolName and ToolQuality, and we have three
data rows within the table. So how do we access these data items in our step definition? If we look at
our step definition here we can see that we've got this parameter of type table and this is a SpecFlow
table class. And we can use this table object to get at the table data in our step definition here. So to get
the firstToolName here we're using indexes, so we're saying the first row and the first column in the first
row. And here we're saying again the first row, but the second column. As well as using indexes for
the columns we can also specify the name of the column. So here we're saying the secondToolName is
at row 1, these being 0 based indexes. And the column is called ToolName. The secondToolQuality
again we're on the second row in the table, and we're looking for the column called ToolQuality. There's
a couple of other methods on this table object to do things such as rename columns and there's
also the useful ContainsColumn method which we can use to test for the existence of a column with a
specified name. So here we're saying does the table contain a column called Color, which it doesn't.
So let's go ahead and debug this test and we'll have a look at this table structure. So we have access to
a collection of Headers, so if we expand this we can see we've got the headers or the columns
of ToolName and ToolQuality. And we also have this Rows property and we can drill down into this, see
each of the table row objects. Each table row object has a collection of keys and values and again
we can just expand this down and we can see we've got ToolName paste and ToolQuality medium. So
let's have a look at our variables here. The first ToolName we've got paste and Quality medium, so this
maps to paste and medium here. And the second ToolName of brush and the second ToolQuality of
high, so again this maps to brush and high. If we look at our IsToolColorColumnSpecified we can see
that we've got false as expected. So that's how we can specify a data table in Gherkin for a Given step
and how we can access the rows and columns by adding a table parameter to our step definition.
So let's take a look at scenario outlines in use. So here we've got our scenario outline from module 2,
we have three parameters, brand, mins, and percent and these map to the columns here. So
notice we've got these quote marks around our string type and these other things are numbers. So let's
go ahead and generate our step definitions, but this time I'm going to start with the Regular
expression style and we'll see why in a just a moment. So let's generate this and let's go and have a
look at our step definitions. So notice that we're using the Regular expression style and for our Given
step for our string it's added our string parameter as expected. So we can put a breakpoint here and we
can debug one of these tests and if we look at our parameter 0 we can see that we get Brand X as
expected. So let's delete these step definitions and let's now generate them using the Method name
underscores style. Let's go and look at our step definitions now so we're not using the
Regular expression style this time. And let's do the same as before, add a breakpoint and then debug
the test. And let's have a look at our parameter value again, but notice this time we've got this additional
quote. We can see that we've got Brand X followed by this quote, which is clearly wrong. So we'll Close
this and stop debugging and let's go back to our Features. And we have a hint here that something is
not quite right. The opening quote is white but the closing quote is gray. When we're not choosing the
Regular expression style we need to remove these quotes. If we debug the test again and check our
string parameter now we can see that we've got Brand X without the additional quote. If we look at our
Feature file we can see that we don't have any gray items now, so this is a hint to us that everything is
as expected. And as with parameters before we can go and make this a bit more readable. We change
this to Brand and the corresponding parameter also to Brand and we could do the same things for these
When and Then parameters as well. So let's just double-check everything is working as expected. So
for this first example in the scenario outline we have our Brand X, if we debug the next one we have our
Brand Y, and if we debug the final one we get our Brand Z. So that's how we can map scenario outline
Because in SpecFlow each of the individual steps in a scenario map to a separate method in our step
definitions sometimes we need to be able to pass data between individual step definitions. One way
we can do this is to use the ScenarioContext. Current and we can use this as a dictionary to add keys
and values to the currently executing scenario context. So when each scenario executes we get a
new scenario context and we can add things to it. So here the key is brand name and the value that
we're storing is the brand that's passed through as a parameter. The next step that gets executed is this
When I brush for so many minutes step. And even though this is a separately executing step we can
retrieve the brand name from the scenario context by specifying as an indexer the key that we added
here. So let's see this in action. We'll debug this test and if we check our brandName variable we can
see that we got Brand X that we stored in the previous step. And we can see this for the other tests.
This time Brand Y was stored. If all our step definitions exist in the same class instead of using the
scenario context we can create private fields. So here we could simply create a field and rather than
use the scenario context we could simply say that we're setting the internal field to the value of this
parameter passed in in this step. And then we could simply use this underscore brand field in this step
here. So just to illustrate, if we run this now we can see that our brand field in the class is set to Brand
Y. So this private field approach is fine if all our steps are within the same class, but because matching
step definitions don't have to be all in the same class, so a scenario could have two steps. One
step could be bound to a step definition in one class and another to a step definition in a different class
we can't always use this approach. And that's when storing things in ScenarioContext. Current is more
flexible.
Module Summary
So that brings us to the end of module 3. In this module we learned how to install the relevant NuGet
packages to get NUnit and SpecFlow working together. We saw the regular expression style of binding
and the method underscore style of binding. We learned how to change the app config to change the
default step definition style. We saw what happens when we add and delete steps. And we saw how we
can execute scenarios and debug individual steps within those scenarios. We saw how a step definition
can be shared between multiple scenarios and how we can parameterize step definitions so we can
reuse them. We saw how to access the data items in a data table when we pass it to a step definition.
And we also saw how when we're using data driven scenario outlines how the data maps to our step
definition parameters. And finally we learned a couple of methods of sharing data between scenario
steps.
Module Overview
Welcome back to Module 4 Testing and Automating a Website with SpecFlow and WatiN. Even though
we're using WatiN in this module to perform the browser automation. If you're using another framework
for your automation such as Selenium or Microsoft Coded UI the processes we'll go through in this
framework you're using. So in this module first off we're going to get an overview of the website that
we're going to be writing our tests for. We'll get a brief introduction to the WatiN automation framework
and we'll go ahead and write a scenario for the password strength feature of the website. We'll then go
and create our automation code for the password strength scenario and also look at how we can
refactor this to improve the code base. We'll look at how we can use WatiN hooks to dispose of
the browser instance. We'll then look how we can refactor our password strength scenario into a
scenario outline to reduce duplication in our feature file. We'll see how we can further simplify our
feature files by introducing a background and finally we'll see how we can refactor our step definitions
So before we get into writing our first scenario let's have a quick look at the demo website that we're
going to create our acceptance tests for. If I just run it we default to this Sign Up page and this is the
feature that we're going to be working with. So this Sign Up page is where a new user would go to
create a new account on this website. So we can type in a username, an email address, and we can hit
Register. And we get some simple validation errors if there's a problem so here the passwords don't
match and we have an invalid email address. So if we fix these two things (Typing) and correct
the password, when we click Register if all of the validation passes then we're taken to this
RegistrationComplete page and we get our username displayed to us. Also notice that we've got this
password strength, so let's go ahead now and create a feature file and our first scenario.
to click buttons and type text into text boxes and you can find out more about WatiN at watin.
org. Alternatives to WatiN also include Selenium and Microsoft's Coded UI. If you want to learn more
about Selenium or Microsoft Coded UI check out the other great courses on Pluralsight. com. We'll be
getting a brief overview of WatiN in just a moment, but if you want to go into more depth check out my
Automated Testing: End to End course. So let's have a look at some WatiN code in action. So here
we've got a NUnit test and the first thing we do is we create a new instance of this WatiN IE class. And
here we're wrapping it in a using statement so that when the instance gets disposed any unmanaged
resources will be cleaned up for us. By default when we lose this IE object the corresponding Internet
Explorer window will also be closed. And for demo purposes I've set this AutoClose property to false so
that our Internet Explorer window stays open and we can see what's happening. Also for demo
purposes I'm making a call to this BringToFrontForDemo extension method that I created just so we
ensure that IE is in the foreground and we can see what's happening when we execute this test.
So now we've got our WatiN object, two enablers to automate the Internet Explorer window, we can
start to work with it. So first off we can navigate to a specific URL by calling the GoTo method. So
here I'm opening up this Register page on my local machine. Once this navigation is complete we can
then work with elements on the page. So for example to find a text field with a given ID we say ie.
TextField and one of the ways we can locate this is to try and find it by the HTML Id. So here we're
saying give me the text field with the Id of Password. When we locate this text field we're just assigning
it to a variable here. So once we've located an element on the page we can interact with it. So for
example with this text box we can use the TypeText method to simulate a user typing text. So here
we're simulating a user typing the words Hello World into this text box. We can work with other
HTML elements such as divs, spans, and buttons as well. So for example if we wanted to click a button
with an Id of DoRegister we'd say ie. Button locate it by its HTML Id and once we've located it, in this
case we're not assigning it to a variable, we're just directly calling its Click method. And this will simulate
a user actually clicking on this button in the browser. So let's go ahead and run this test and see WatiN
in action. So we quickly saw then how the string Hello World was typed into the password box and how
the Register Now button was clicked. And because we've got some validation errors here we've just got
the validation messages appearing here. So that's how we can create an instance of the WatiN IE class
and use it to navigate to a specific URL and then interact with the HTML elements that exist on the
page.
Let's go ahead now and create our feature file. So for example we can think of the feature that we're
working with as the new user registration feature. So first off let's go and create a feature file and we'll
call it NewUserRegistration. So let's remove this default scenario and just tidy things up. So first off
let's add some description of the feature in this free text section and we'll use the in order to as an I
want format. So let's start with In order to get access to the member only Features As a potential
new user I want to create an account. So let's add the first scenario to this Feature. So we'll call this
Scenario Password strength indicator and in this Scenario we'll be checking that when the user
types different lengths of password that we get the correct password strength indicated to them. So let's
start with our Given, so we can say Given I'm on the registration page, When I enter a password of
Pass, Then the password strength indicator should read Poor. We could also apply a tag to our
Scenario or our Feature to indicate that it belongs to a superset of logical features. So here we can
example, to run all scenarios related to authentication, so this could also include things like logging into
So now we have our Feature file and our first Scenario, let's go ahead and create the initial version of
our automation with WatiN. So first off let's generate our step definitions and I've already set this project
first off let's go and install WatiN from NuGet. And here we'll install the WatiN package. And just Close
this and we'll add a using statement to WatiN. Core and we'll just remove this pending line here. So the
first thing we need to do is create a new IE object, but this time we can't create it with a using because
we don't want to dispose the IE instance as we need to use it in later steps. So first off let's just create
the instance and here we're using an overload of the constructor that allows us to specify the starting
URL. So here we're just going to the register page. And as in the WatiN overview I'm just going to make
Internet Explorer. And I'm just going to include this extension method in our project. So let's just build
this and we've just got a build error as I haven't included the namespace for our extension method.
So let's just grab this namespace and add it here. Now we can build and notice we get our
PasswordStrengthIndicatorTest for our scenario here. I'm just going to change the view in Test Explorer
to the project view so we can just group all of our scenarios differently from our overview of WatiN test
that we used earlier. So let's go ahead and execute this first test. And we want to see if Internet
Explorer opens correctly and it's on the right page. So we've got an error here when we tried to execute
the test. If we have a look at it we can see that it's making reference to this Interop. SHDocView and
that's because when we use WatiN we have to edit this reference and change the Embed
Interop Types property from True to False. And we can just build to make sure there's no errors and we
can try and run our test again. Notice that the test has failed again but for a different reason.
When we're using WatiN to automate Internet Explorer our tests need to execute in the single threaded
apartment threading model. So if we head to our AssemblyInfo we can change this by adding the
RequiresSTA attribute to our assembly. And this is an attribute that comes from the NUnit Framework.
This attribute instructs NUnit to execute tests using a single threaded apartment model. So let's close
this and build and now when we run our test we should see Internet Explorer open at the correct page.
And it does. So here we have our page Register. aspx loaded in Internet Explorer. So if we come back
to our automation steps, because we need to use this ie instance in our other steps we need
somewhere to store it. So we can use our scenario context to do this. Here we're adding our ie instance
to the ScenarioContext. Current with a key of Browser. Our next step is to enter the password of Pass
into the password text field. So first off we need to get a reference to the ie instance stored in
our ScenarioContext from the previous step. Next we want to locate the correct text box that represents
the password, so we're just using the FindById method and we're going to type the string Pass into it.
So notice here that we're just hard coding Pass as opposed to using a parameter from the step
definition. And we're going to perform some refactoring's later in the module. Unfortunately there's a bug
in WatiN that sometimes prevents events from firing on our HTML elements. So when we type a
password into the password text box every time we press a key a JavaScript event fires that updates
the password strength, but when we're using WatiN to type the text this bug means that the key press
event in JavaScript isn't fired. So as a workaround for this we can use the Eval method of the ie object
to evaluate some JavaScript in the browser. We're using jQuery here to select the password box and
force the firing of the key press event. And we can just add a comment here to explain things. So let's
go ahead and execute this test now with our second step. And we can see that our password of Pass
has been typed into the password box and our password strength is indicated to us. The final step is to
actually check that the password strength indicator reads correctly. So we expect it to read Poor. So as
before we get a reference to our ie object and the next thing we need to do is read out the string for the
password strength. So here we're locating a Div with the Id of PasswordStrength and we're just getting
its InnerHtml and assigning this to the strength variable. So now we've got the password strength
that's being displayed in the browser, we need to make a NUnit assert to check that it's correct. So here
we're expecting the string Poor and passing the actual string. Now we're at the end of this scenario,
we can dispose of our ie instance, but before we do this we just want to set AutoClose to false so we
can see what's happened in the browser for demo purposes. So let's go ahead and execute this
complete scenario now and see what our result is, but first just let's quickly add the NUnit
framework using statement so we can access the assert method and now run. And we can see that our
password has been typed in as before and the password strength is displayed as before, but if we head
back to Visual Studio now we can see that our test has passed. And to prove this is working let's
change the Assert and run the test again. And let's check our test result. And we can see this time
it's failed because we expected xPoor but was actually Poor. So let's just fix this up and Save and just
Whilst we have our basic automation working now we can make some improvements to the actual code
and perform some refactoring. So notice in these subsequent steps each time we have to go and grab
the ie instance from the ScenarioContext. So we have some code duplication here that we can take
care of. Also whilst we've only got one step definition creating an ie instance at the moment, if we had
to manually create an ie instance in the first step definition of every scenario we had, this would also
create some code duplication. So let's see how we can start to refactor things. If we go to our Solution
Explorer and I've added this WebBrowser class to our test project. If we have a look at this we can see
that we've got a public static class called WebBrowser and we expose a current property of type IE.
When we access this property getter we first check that the scenario context doesn't contain a browser,
if it does then we just return it, but if it doesn't contain the browser key we go ahead and create a new
one. And for demo purposes we just tell it not to close and bring it to the foreground. Once we've
created our Internet Explorer object we then just go and add it to the ScenarioContext. So now we've
got this new WebBrowser class that will deal with the creation and storing of ie instance for our
ScenarioContext, let's go and refactor our existing steps. So first off we can get rid of this line. So in the
first step here we still need to navigate to the registration page and to do this we can access our
WebBrowser. Current property, which will give us our ie instance. And then on this ie instance we can
call the GoTo method which will navigate to our register page. We can also get rid of these two lines
because in our WebBrowser class we're already bringing Internet Explorer to the foreground. And we
also no longer need to store our browser manually in our ScenarioContext. Again the WebBrowser
class is taking care of this for us, so let's delete these and we can see already that our step
Explorer object and we can replace this local ie variable with a reference to WebBrowser. Current. And
the rest of it stays the same as before. We still need to handle the firing of this key press event, but
again we can just replace this ie with WebBrowser. Current. Let's go to our Then step, again
remove this and we can replace our ie reference with WebBrowser. Current. We keep our Assert the
same, we can also delete our AutoClose line and replace ie Dispose with WebBrowser. Current
Dispose. So let's build this to check there's no errors. And run this test now. And we can see
our automation in progress and we can see that in Visual Studio our test passed.
Current Dispose in our last step of our step definitions. We don't really want to have to worry
about disposing the WebBrowser. Current ie instance in the end of every Then step that we write.
SpecFlow provides us with a number of hooks that we can use to help us out in this instance. If we take
a look at the SpecFlow documentation at specflow. org we can see we have these attributes. Before
and after test run, before and after feature, before and after scenario, before and after scenario block,
and before and after steps. So let's take a look at how we can use the before scenario and after
scenario hooks. So I've added this ScenarioBeforeAndAfter class. If we take a look at it we can see that
I've decorated this class with the SpecFlow binding attribute and this tells SpecFlow that it should look
inside this class for bindings including these hooks. So here we've got this BeforeScenario attribute
or we could use the shortened version of Before and we're just applying this to a static void method. So
this method will be executed before each and every scenario starts and also before each and every
data example in a scenario outline. Similarly the AfterScenario attribute means that this After method
will get executed after each scenario finishes executing. So in this method I've added a call
to WebBrowser. Current Dispose so this will mean that after every scenario finishes executing the ie
instance will be disposed of. So we can now head back to our steps and remove this dispose line here.
So I've got the breakpoint in this After method and let's go and debug this test. So we briefly saw
Internet Explorer open then, if we pop back to it we can see our automation has taken place, Pass has
been entered, and if we hop back to Visual Studio we can see that our breakpoint has been hit in our
After method. So we can just continue this. So that's how we can use the BeforeScenario and
AfterScenario attributes to execute code before each scenario executes and also after each scenario
So now we've got our first scenario automated for password strength, we need to add more scenarios
to cover the password strengths of not only Poor but Average and Awesome. So we could go and copy
this scenario and duplicate it another two times and then change our scenario steps to use parameters
for the password and for the expected strength, but in this instance a scenario outline is a better choice.
As it will result in less duplication in our Feature file. So let's go ahead and replace this scenario with
a scenario outline. So here we've replaced the password that we type in and also our expected strength
indicator. And we've got three examples here pass where we expect Poor, Password where we expect
Average, and long password where we expect Awesome. So we need to change our existing
step definitions for our When and Then lines to take account of these parameters that we need to pass
in. So let's start with this When line. So here we're going to add a parameter and rather than typing
this fixed string literal we'll now type the password that was passed to the step definition. Similarly for
our Then step definition we'll add a parameter and we'll just rename this existing strength parameter as
ActualStrength and our expected strength will now be the strength passed in to this step definition via
the parameter that's mapped here. So if we Save this and check our Feature file notice now that both
password and strength have changed to italics to indicate that they're mapped to our example
columns password and strength. So let's build this and we can see now we get our three scenario
examples and let's go ahead and run all three. So that's our three scenarios executed and we're just
leaving the Internet Explorer windows open again, but if we head back to Visual Studio we can see that
our three scenario examples were executed and they all passed. So that's an example of how we can
use scenario outlines and pass the data provided in the step definitions parameters via WatiN to the
browser.
Let's now explore our second scenario. So here I've added a new Scenario for the username already in
use. So here we're saying Given I'm on the registration page, When I enter valid new user details,
But the username Mr. Awesome is already taken, When I try to proceed with registration, Then I should
see an error sorry that username is already in use. So notice here that I've chosen to use two When
lines. So I could have written this When I enter valid new user details, But the username Mr. Awesome
is already taken, And I try to proceed with registration, but I think having this second When instead of an
And makes the Scenario read a little better, but we could certainly use an And if we wanted to. Now
we've started to add additional Scenarios, we can move this tag that's currently only applicable to
this Scenario outline to the Feature level. And now all of the Scenarios within this Feature will inherit this
tag. So I've already implemented the step definitions for our username already in use scenario and
notice here that the first step definition, Given I'm on the registration page, is shared with the Scenario
outline above, but the remainder of this Scenario I've created new step definitions for. So let's check out
this When I enter valid new user details first. So this step affectively fills out our form with valid data.
And here I'm creating a random username by just grabbing the first 10 characters from a NewGuid. We
then type this random username into the UserName text field, we enter an EmailAddress and we enter
the Password and the ConfirmPassword. The next step But the username Mr. Awesome is already
taken we've got something extra going on here. So we've got a parameter for the username being
passed into this step definition and we're typing in this username into the UserName text field, but
notice we're doing the same thing here so there's a bit of duplication, but this means that we can reuse
this step definition anytime we need to enter valid new user details into the form. So when the
username name is already taken, because we're passing in variable data here we want to ensure that
the system has a user setup for this name. So that when we come down to check that the right
validation message is displayed we can be sure that the state of the system, ie the username exists, is
as expected before we try and perform the user registration. So we're calling this CreateUser method
on this test data class and this is a class that I've created. If we look at this CreateUser method for
demo purposes the website is hard coded to recognize the username Mr. Awesome as it being an
existing username, but obviously in a real system the users would be stored in some backend storage
system for example a database. So to ensure that this username exists we could either directly insert it
into the underlying user database table, we could go via the app service layer to create a user,
or alternatively we could do it through the UI. So the UI will be the slowest method here, the next
slowest will probably be the service layer, and the fastest will probably be the direct insert into
the database. So depending on the system you're building you'll have a number of different options that
may suite you. So once we've ensured that our username exists in the system we then go and override
the username in the web page. The next step is the When I try to proceed with registration. If we have a
look at this we can see here that we're simply clicking the button called DoRegister, but notice in the
Feature we don't actually say When I click the registration button. And this is because wherever
possible in our scenarios we want to try and be as user interface agnostic as possible. The final step
Then I should see an error. Here we're finding the list of validation errors with the Id of
ValidationErrorList and we're simply saying all of the text within this list check if it contains the message
passed through as a parameter to this step definition. And then we're simply asserting that this is true
and if it's not true we're providing a more helpful failure message. So here we're just saying that the
message was not found in validation errors. So let's execute this and see it in action. So our automation
has finished, let's go back and check that we got a passing test, which we did. And we can double-
check this is working by changing the error message here and executing it again. So now when the test
runs the error message that we see will be different from the error message that we expect to see. So
let's head back to Visual Studio and we can see now that our test has failed with our helpful message
that Sorry yyythat username is already in use not found in validation errors.
So now we've got a couple of Scenarios in our Feature file, we can start to think about if we can remove
any duplication from our Scenarios. If we have a look at our two Scenarios here we can see that we've
got this duplicated Given I'm on the registration page step. And if we look at the description of our
Feature it's likely that all of the Scenarios within this Feature file will start on the registration page. So
we'd have this Given I'm on the registration page step at the start of each one of our Scenarios, but
we can prevent this duplication by introducing a background to our Feature. So let's go ahead and do
that and we'll just grab the Given line here and add it as a background step. We can then remove it from
this Scenario as well and now the background for all of the Scenarios in this Feature states that Given
I'm on the registration page. If we build this there's no errors and we don't have to change any of our
step definitions even though we've moved this Given line to the background, it still maps to the same
step definition, which we can see here. So let's go ahead and execute our username already in use.
And we can see our Scenario executes the same as it did before. So when we use a background for
a Feature the background runs before each and every Scenario executes. And in the case of a
Scenario outline it will execute before each example in the Scenario outline, but the background will
execute after any Before hooks we've set up. So for example, our BeforeScenario hook here will be
executed first, followed by our background, and then the Scenario. So I've got a breakpoint here and a
breakpoint in this first Given step. If we go and debug this test we can see that the first breakpoint that's
hit is in our BeforeScenario hook, if we continue we can see now that we get our background
step executed next. And if we continue execution we'll get our Scenario executed. So that's how we can
use a background to execute one or more steps that are relevant and add context to each of the
As the number of Features and Scenarios in the test automation grows larger we might want to give
more thought about how our step definitions are organized. So at the minute we've got a single Feature
and a single matching step definition file. So in our Feature this Given I'm on the registration page step
we could consider this to be part of a logical set of navigation steps. So we could move its matching
step definition into a separate steps class. So let's go ahead and do this. So we'll Add a new Class and
we'll call it NavigationSteps. If we have a look at our existing steps we can now remove this Given and
paste it into our NavigationSteps file, but if we look here we also need to add the SpecFlow using and if
we look at our steps we can see that we use this Binding attribute of a public class, so we'll replicate this
in our NavigationSteps and we'll just build to check everything's okay, which it is. And if we go back to
our Feature now we can see that this Given line is still in white, which means SpecFlow is still finding a
matching step definition. It's just this time it's finding it in this NavigationSteps class. So if we run
our Scenario now we can see that it's executing as before, even though we've moved the step definition
into a separate class. If we were to remove this Binding attribute and build and check our Feature now
we can see that the Given step has changed to purple because SpecFlow can't find a matching step
definition. So let's just go and quickly add that back. So that's how we can start to organize our steps
into logical groupings inside our own classes as the number of scenarios grows over time.
The final thing we're going to address in this module is the fact that in our step code here we have the
Ids of the controls we're interacting with duplicated throughout this code. So here we're finding the
have the Ids littered throughout our steps and as we continue to add Scenarios to our Feature this
proliferation of duplicated Ids will increase. So this is a problem because if the page we're
testing changes it's Ids for whatever reason then our automation will break. For example if the register
button changed its Id from DoRegister to Register then when we try and execute this step the button
would not be able to be located and so the button wouldn't be clicked and we'd get an error. So we
could go and create some constants either in this steps class or perhaps in a global Ids class, but a
better way is to create a class that models the registration page. So I've already created this class and
we can use this to abstract a way the underlying Ids of the page we want to automate. So the first thing
we do here is inherit from the WatiN page class and here we're just defining a number of constants to
represent the Ids of the controls we want to interact with. Then we simply create a property, in this case
for UserName, we create a get and a set and the get property locates the TextField with the Id specified
in our UserNameId constant and just returns its Text. And in the setter we're finding it and using
the TypeText method to enter text into the field. And we're doing a similar thing for the EmailAddress,
just a Div that we're reading the content from. And similarly with the ValidationErrorListText we're
just returning the Text of the list. We also have a method called ClickRegisterButton and again this
decouples the actual button Id from our automation code. And in this example rather than creating a
constant we've just got the string literal. So the benefit of this approach is we've effectively decoupled
the Ids from our automation code and we'll see in a moment how it also makes our automation code
more readable. We can also do things like where we set the Password before in our automation step
we had to execute this Eval statement, but we can now encapsulate this in the setter for the
password property. So once we type the new password we call the Eval. So let's go ahead and look at
our steps. So I'm going to go and replace all of these steps with a version that I've modified to use this
new registration page class. So let's compare the old and the new. Before in
our When_I_enter_a_password step we had this WebBrowser. Current we're finding the Id and then
we're calling Eval to force a key press event, but now we're using these two lines. So when we create a
class that inherits from the WatiN Page object it allows us to use the Page method of the ie object to
interact with the current page that we're on in a strongly typed way. So here we're just saying we're on
the registration page and we represent this with a variable, in this case p. Once we have this page we
can use its properties and methods. So here we're just setting the Password property and when we do
this the setter will be called and the value we set the property to will be typed in to the password field.
And because we've encapsulated this Eval as well we no longer need to do this in our step definition.
The next step here, again we start by getting our page, but whereas before we had our actual strength
now we just set actualStrength to the read-only PasswordStrength property of the page. And let's just
creating our random UserName grabbing our page, but rather than these four lines here we replace
them with these four lines here. Again using the properties of the page. So you can see here that it's a
method rather than having to find the button and click it ourselves. And in our error message step here
rather than having to perform this kind of work ourselves we just access the ValidationErrorListText
property and check it contains our message. So by using this registration page we've also isolated
the Ids of controls and improved the readability of our step definitions. So let's build this to check there's
no errors and let's go and run all of our scenarios to check that they still work. So we can see our
password strength scenarios executing and our existing username scenario, so let's head back
to Visual Studio and we can see that our tests still pass. So that's an example of how we can refactor
our step definition code to use a strongly typed page model. There's a couple of different
conceptual styles we can use when we represent pages, if you're interested in learning more about
these styles check out my Automated Testing: End to End course and have a look at the Functional UI
Testing Module. And in this module there's also more information about the WatiN framework.
Module Summary
So that brings us to the end of this module on SpecFlow and WatiN. In this module we saw an overview
of the website under test. We took a brief tour of the WatiN automation framework. And we wrote the
first password strength scenario. We then learned how to create our automation code for this scenario
and to refactor the code to improve readability and reduce duplication. We saw how we can use
hooks such as the before and after hooks to dispose of the browser instance. And we then refactored
our password strength scenario to use a scenario outline. We saw how we can use backgrounds to
further reduce duplication in our scenarios. And finally how we can move step definitions into other