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