You are on page 1of 15

Testing and Automating a Website with SpecFlow and

WatiN

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

module such as refactoring our scenarios and step definitions apply regardless of which automation

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

into other classes so we can organize them in a more logical fashion.

Overview of The Website under Test

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

1
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.

A brief Introduction to WatiN

WatiN is an open-source framework that allows us to automate Internet Explorer browsers. For example

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

2
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.

Writing The Password Strength Scenario

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

describe this Scenario as belonging to the authentication superset. So this would enable us, for

example, to run all scenarios related to authentication, so this could also include things like logging into

the site and resetting passwords.

3
Writing Automation for The Password Strength Scenario

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

to use the default for underscores. So here's our step definitions. So our first

step Given_I_m_on_the_registration_page we need to use WatiN to navigate IE to the correct page. So

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

a call to the BringToFrontForDemo extension method so we can see what's going on in

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.

4
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

5
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

double-check and our test now passes again.

Refactoring Password Strength Automation Code

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

6
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

definition automation code is starting to look a lot simpler. So for

our When_I_enter_a_password_of_Pass step we can again get rid of this retrieval of the Internet

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.

Using hooks to Dispose The Browser Instance

So we've made some improvements to our automation code, but we still have this WebBrowser.

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

7
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

has finished executing.

Refactoring into A Scenario Outline

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

8
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.

Writing The Existing user Scenario

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

9
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

10
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.

Refactoring Features to use Backgrounds

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

Scenarios in our Feature.

11
Refactoring step Definitions into other Classes

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.

Refactoring Browser Interaction into A Page Model

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

TextFieldById of Password and again this Id is used in this Eval statement. We've got

the PasswordStrengthId, the UserName, Email, Password, ConfirmPassword Ids and so on. So we

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

12
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,

Password, ConfirmPassword, but when we get to PasswordStrength we don't have a setter because it's

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

13
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

go and add this Assert back in. For the When_I_enter_a_valid_new_user_details step we're still

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

lot less cluttered than the original version. When we get down

to the When_I_try_to_proceed_with_registration step definition we simply call the ClickRegisterButton

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

14
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

classes that have the binding attribute applied.

15

You might also like