Powered By Blogger

Search Here!

Tuesday, June 5, 2012

Behaviour Driven Development (BDD) in .Net using SpecFlow, Selenium RC and MS Test !

BDD Using SpecFlow using .Net and MS TEST

I have been working on the Behaviour Driven Development (BDD) using SpecFlow for .Net and I felt it is fun. According to me, it is a Tester’s tool rather than a Developer’s tool and I felt it is worth sharing. Kindly refer the wiki for more theoretical info on BDD: http://en.wikipedia.org/wiki/Behavior_Driven_Development


System Requirements:
Windows 7 as OS
Microsoft Visual Studio 2010 as IDE
SpecFlow as BDD tool for .Net
Selenium as a Test Automation Tool

Referencing the SpecFlow:
1. Install the SpecFlow software on your machine.
2. In Visual Studio, Go to the Project -> Add Reference menu item.
3. When the Add Reference dialog appears, click on ‘Browse’ and navigate to C:\Program Files (x86)\TechTalk\SpecFlow and select TechTalk.SpecFlow.dll.
4. Finally, the solution Explorer should look like this:

Consider a sample user story:

Feature: User Creation
In order to Create user
As a Workspace admin
I want to create a w s user

Let me write a test scenario for the above story:

Scenario Create New User

When I enter username in the username field
And I enter password in the password field
And I press Login
Then the response Page is Course Enrolment
When I click the Create New User link
User popup
When I click the Save button
Then It s
And Enter the user details in Create Ne
would display successful message New user created successfully

Creating a new SpecFlow feature file:

1. Context-click on the project name ‘SpecflowTest’ in the Solution Explorer
2. Select Add -> New Item
3. Select the ‘SpecFlow Feature File’ and save it as ‘_Feature.CreateNewUser.feature’
4. Specflow creates a new file “ _Feature.CreateNewUser.feature ” and a designer file “ _Feature.CreateNewUser.feature.cs“. The default content of the Specflow file is in the Gherkin format.
5. The default content of the file is as shown is below:

Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers

@mytag
Scenario: Add two numbers

Given I have entered 50 into the calculator
And I have entered 70 into the calculator
When I press add
Then the result should be 120 on the screen

6. Change the contents of the file to the new scenario as below and the Specflow generates a feature file ‘ _Feature.CreateNewUser.feature.cs ’:

Add Scenario according to your Feature:

Feature: User Creation
In order to Create user
As a Workspace admin
I want to create a ws user

Background:
Given I open frmLogin.aspx?mode=admin in the browser

@sprint
@alternative_syntax
Scenario Outline: Create New User
When I enter in the username field
And I enter in the password field
And I press Login
Then the response Page is Course Enrollment
Examples:
|username |password|
|sel_ws_admin|password2|
When I click the Create New User link
And Enter the user details in Create New User popup
| Field | Value |
| txtPassword | password1 |
| txtFirstName | QA |
| txtLastName | BDD |
| txtEmail | bdd@gmail.com |
When I click the Save button
Then It should display successful message New user created successfully


1. Rename the class ‘Class1.cs’ to ‘ _Feature.CreateNewUser.cs’.
2. Context-click on the feature file ‘GoogleSearch.feature’ and select …… System generates a specification file ‘…..’ as shown below.


[Binding]
public class CreateNewUserDefinitions : SeleniumStepsBase
{
// Purpose: Creation Readonly Object(s)
readonly NewUserPage _newUserPage = new NewUserPage();

#region Locator
private const string Button_Signin = "//button[@class='lgn_btn']";
#endregion Locator

// Purpose: Step Implemented To Enter Username For Ws Admin
[When(@"I enter (.*) in the username field")]
[Given(@"I enter (.*) in the username field")]
public void EnterSomethingIntoThUsernameField(string username)
{
// Purpose: Calling Method To Enter Username in Username Field For Ws Admin
Selenium.Type("username", username);
ScenarioContext.Current["username"] = username;
}

// Purpose: Step Implemented To Enter password For Ws Admin
[When(@"I enter (.*) in the password field")]
[Given(@"I enter (.*) in the password field")]
public void EnterSomethingIntoThePasswordField(string password)
{
// Purpose: Selenium Method To Enter Password For Ws Admin in Password Field
Selenium.Type("password", password);
}


// Purpose: Step Implemented To Click on SignIn Button For Ws Admin
[When("I press Login")]
[Given("I press Login")]
public void WhenIPressLogin()
{
// Purpose: Selenium Method To Click on SignIn Button For Ws Admin
Selenium.Click(Button_Signin);
Selenium.WaitForPageToLoad(ConfigurationManager.AppSettings["Timeout"]);
}


// Purpose: Step Implemented To Click on 'Create New User' link in Ws
[When(@"I click the Create New User link")]
public void WhenIClickTheCreateNewUserLink()
{
// Purpose: Calling Method To Click on 'Create New user' Link in Ws
new AdminToolPage().StepClickCreateNewUserLinkWs();
}


// Purpose: Step Implemented To Fill User Details
[When(@"Enter the user details in Create New User popup")]
public void WhenEnterTheUserDetailsInCreateNewUsePopup(Table table)
{
// Purpose: Calling Method To Fill User Details
_newUserPage.StepFillUserNameInWs();
foreach (var tableRow in table.Rows)
{
Selenium.Type("//input[@id='" + tableRow["Field"] + "']", tableRow["Value"]);
}
}

// Purpose: Step Implemented To Click on Save Button for User Creation
[When(@"I click the Save button")]
public void WhenIClickTheSaveButton()
{
// Purpose: Calling Method To Click on Save Button for User Creation
_newUserPage.StepClickUserSaveButtonInWs();
}

// Purpose: Step Implemented To Verify Success Message on User Creation in Ws
[Then(@"It should display successful message New user created successfully")]
public void ThenItShouldDisplaySuccessfulMessage()
{
// Purpose: Caling Method To Verify Success Message on User Creation in Ws
new AdminToolPage().StepSuccessMessageOnUserCreatedWs();
}
}
}

Creating a Static class for defining the browser selection:


[Binding]
public static class SeleniumSupport
{
private static ISelenium _selenium;

// Purpose: Method Implemented To Get The Status of Test Scenario Web Seesion
private static bool ReuseWebSession
{
get { return ConfigurationManager.AppSettings["ReuseWebSession"] != "true"; }
}

// Purpose: Action Taken by Before Test Scenario Starts
[BeforeScenario("sprint")]
public static void BeforeWebScenario()
{
// Purpose: Starts Selenium Instance Before Test Scenario Starts
StartSelenium();
}

// Purpose: Action Taken by After Test Scenario Ends
[AfterScenario("sprint")]
public static void AfterWebScenario()
{
// Purpose: Stop Selenium Instance After Test Scenario Ends
if (ReuseWebSession)
StopSelenium();

Assert.AreEqual("", ScenarioContext.Current.SeleniumErrors().ToString(), "Selenium verification errors");
}


// Purpose: Method Implemented To Start Selenium Instance
public static void StartSelenium()
{
SeleniumProcess.Start();
if (_selenium == null)
{
if (Playback.IsInitialized)
{
Playback.Cleanup();
}
Playback.Initialize();
_selenium = new DefaultSelenium("localhost", 4444, ConfigurationManager.AppSettings["browser"], ConfigurationManager.AppSettings["browserUrl"]);
_selenium.Start();
try
{
_selenium.Open(ConfigurationManager.AppSettings["browserUrl"]);
_selenium.WindowMaximize();
}
catch (Exception e)
{
throw new Exception("Uanble To Start Selenium Server with Base Url: Please check your network settings.");
}
}

ScenarioContext.Current.SetSelenium(_selenium);
}


// Purpose: Method Implemented To Stop Selenium Instance
public static void StopSelenium()
{
if (_selenium == null)
return;

try
{
_selenium.DeleteAllVisibleCookies();
_selenium.Stop();
_selenium.Close();
}
catch (Exception ex)
{
Debug.WriteLine(ex, "Selenium stop error");
}
_selenium = null;
Console.WriteLine("-> Selenium stopped");
SeleniumProcess.Stop();
}
}

// Purpose: Method Implemented To Web Test Scenarios
public static class ScenarioContextWebExtensions
{
public static ISelenium Selenium(this ScenarioContext scenarioContext)
{
try
{
var result = (ISelenium)ScenarioContext.Current["selenium"];
return result;
}
catch (Exception)
{
throw new Exception("selenium is not started");
}
}

// Purpose: Method Implemented To Get The Selenium Instance in Current Test Scenario
public static bool IsSeleniumRunning(this ScenarioContext scenarioContext)
{
return ScenarioContext.Current["selenium"] != null;
}

// Purpose: Method Implemented To Set Selenium Property
public static void SetSelenium(this ScenarioContext scenarioContext, ISelenium selenium)
{
ScenarioContext.Current["selenium"] = selenium;
ScenarioContext.Current["selenium-errors"] = new StringBuilder();
ScenarioContext.Current["StepCounter"] = 0;
}

// Purpose: Method Implemented To Handle Selenium Unexpected Error(s)
public static StringBuilder SeleniumErrors(this ScenarioContext scenarioContext)
{
var result = (StringBuilder)ScenarioContext.Current["selenium-errors"];
Assert.IsNotNull(result, "selenium is not started");
return result;
}
}

Executing the Tests: MS Test

For using MS Test, we need to add configuration in app.config
<configSections>
<section name="specFlow" type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler, TechTalk.SpecFlow"/>
configSections>

<specFlow>
<language feature="en-US" />
<unitTestProvider name="MsTest.2010" />
<runtime detectAmbiguousMatches="true"
stopAtFirstError="false"
missingOrPendingStepsOutcome="Inconclusive" />


<trace traceSuccessfulSteps="true"
traceTimings="false"
minTracedDuration="0:0:0.1" />
specFlow>

No comments:

Post a Comment