Saturday, October 19

Basic SpecFlow Automation tests in visual studio express using C# ,Selenium webdriver,nUnit console


Specflow is layer that would sit on the top of C# and one can specify tests cases in normal English readable format and build automation using these test cases. Writing test cases in format that is easily readable enables appropriate reporting and the direct mapping user story into automated test cases.Instead of digging down what test case is doing, here from reading the test case itself, it gives understanding of what it is about and what it is doing. Thus enables one to describe a test case on how the desired behaviour should be specified. 
Using above  behaviour specification structure one can write a story like
Feature: Google Search
As an end user,
I would like to visit the google search page
And then I would like to search an item so that
I can view the search results

Scenario: Google Search
Given I am on the Google home page
When I search for text Webdriver
Then I should see the search results with title WebDriver – Google Search


Now the above defined test cases needs to glued to test automation code such that each of above Given When Then steps would get executed and make appropriate calls to application (browser here). Specflow helps to achieve this gluing of the code to BDD structure test cases. Lets see how we can do this using C#, selenium webdriver and nUnit. If you have not done setup of these tools, in my previous blogs I had describe how to setup Visual studio express with nUnit, selenium (or refer my another blog where I have explained how to install NuGet and download selenium/nunit etc)and how to create basic script. You can refer these blogs if you have not done the setup and want primary understanding on Selenium/C# and nUnit work together.

1. Setting up specflow and generate C# file having gluing code

Download Specflow binaries from https://github.com/techtalk/SpecFlow/downloads which will have name in form of Specflow_V<versionnumber>_bin.Zip. Extract this file into location like “C:\Data\seleniumCsharp\”. Once extracted, we can now add the path of binary exe file in PATH variable of windows environment. For this go to Start –> Rightclick on Computer –> Select properties. Next Click on Advance settings . If you are using windows 7, you will be prompted to enter admin password. Next click on Environment variables.  Under System variables edit the PATH variable by  appending the path something like “C:\Data\seleniumCsharp\SpecFlow_v1.9.0_bin\tools\” with semi colons as separator
After this open a new command window and enter specflow and then you should see following output in command window  which confirms specflow exe is been located using system PATH
C:\>SpecFlow
usage: specflow <subcommand> [args]
Type 'specflow help <subcommand>' for help on a specific subcommand.

Available subcommands:
generateall - Generate tests from all feature files in a project
stepdefinitionreport - Generates a report about usage and binding of steps
nunitexecutionreport - Formats an NUnit execution report to SpecFlow style
mstestexecutionreport - Formats an MsTest execution report to SpecFlow style


From above output , there are two subcommands which we are interested  - generateall and nunitexecutionreport. Using subcommand generateall lets try to create step definition class file in C#. 
  • Go to Visual studio express(if you dont have one download it from Download Visual studio express for Windows Desktop), and create file –New project – SpecflowTutorial.
  • Open a new project of type Class library. Open Tools –>Library Package Manager Console > Package Manager Console to open NuGet. In the powershell command window type following command to download the packages
    Install-Package SpecFlow
    Install-Package SpecFlow.NUnit
    Install-Package Selenium.WebDriver
    Install-Package Selenium.Support
    If you do not have NuGet refer my previous blog where I have explained how to install NuGet and download selenium/nunit etc
  • Next copy the feature and scenario marked in bold into a txt file and rename its extenstion from “.txt” to “.feature” (for example Google.feature). Save this file in project directory which is same location as .csproj file is stored for your project. Then in solution explorer of VS Express add this file by right clicking project –> Add –> Existing item. Change the filter to All files and you should see the new feature file. Then select the file and click add. Next save the project as SpecflowTutorial.
  • First I will show you how to create a step definition file using the specflow command and then we can create a batch file that can be executed from external tools which helps us to generate step definitions in just one click.  Navigate to project directory where your .csproj and .feature files are stored and copy the path.
  • Next open a command window and type
specflow generateall <ProjectPath>\<Projectname.csproj> /force /verbose
where <Project Path> is the path that you copied in previous step and <Projectname.csproj> is the name of the project and click enter. You should get following output in command window
Processing project: SpecflowTutorial
Google.feature -> test updated
  • After this click on project in solution explorer –> Add –> Existing item and select All files filter . You should see a file with same name a feature but .cs extension. Select this file and click Add. Save the project.
  • Open up this .cs file which has feature file name a prefix (for example Google.feature.cs) and you can see a gluing code that joins the feature file. This file looks like as below.
// ------------------------------------------------------------------------------//  <auto-generated>//      This code was generated by SpecFlow (http://www.specflow.org/).//      SpecFlow Version:1.9.0.77//      SpecFlow Generator Version:1.9.0.0//      Runtime Version:4.0.30319.18052// //      Changes to this file may cause incorrect behavior and will be lost if//      the code is regenerated.//  </auto-generated>// ------------------------------------------------------------------------------#region Designer generated code
#pragma warning disable
namespace SpecflowTutorial
{
    using TechTalk.SpecFlow;
    
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
    [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [NUnit.Framework.TestFixtureAttribute()]
    [NUnit.Framework.DescriptionAttribute("Google Search")]
    public partial class GoogleSearchFeature
    {
        
        private static TechTalk.SpecFlow.ITestRunner testRunner;
        
#line 1 "Google.feature"#line hidden
        
        [NUnit.Framework.TestFixtureSetUpAttribute()]
        public virtual void FeatureSetup()
        {
            testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
            TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Google Search", "As an end user,\r\nI would like to visit the google search page\r\nAnd then I would l" +
                    "ike to search an item so that\r\nI can view the search results", ProgrammingLanguage.CSharp, ((string[])(null)));
            testRunner.OnFeatureStart(featureInfo);
        }
        
        [NUnit.Framework.TestFixtureTearDownAttribute()]
        public virtual void FeatureTearDown()
        {
            testRunner.OnFeatureEnd();
            testRunner = null;
        }
        
        [NUnit.Framework.SetUpAttribute()]
        public virtual void TestInitialize()
        {
        }
        
        [NUnit.Framework.TearDownAttribute()]
        public virtual void ScenarioTearDown()
        {
            testRunner.OnScenarioEnd();
        }
        
        public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
        {
            testRunner.OnScenarioStart(scenarioInfo);
        }
        
        public virtual void ScenarioCleanup()
        {
            testRunner.CollectScenarioErrors();
        }
        
        [NUnit.Framework.TestAttribute()]
        [NUnit.Framework.DescriptionAttribute("Google Search")]
        public virtual void GoogleSearch()
        {
            TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Google Search", ((string[])(null)));
#line 7
this.ScenarioSetup(scenarioInfo);
#line 8
testRunner.Given("I am on the Google home page", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 9
testRunner.When("I search for text Webdriver", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 10
testRunner.Then("I should see the search results with title WebDriver – Google Search", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
            this.ScenarioCleanup();
        }
    }
}
#pragma warning restore
#endregion

It is not necessary to understand the complete code for time being, but few things to point out. You can see in method name GoogleSearch(), there are method calls with name testRunner.Given, testRunner.When and testRunner.Then. The text inside these method calls is the text we need to use when we create step definitions

2. Writing step definitions

Click on Project in solution explorer and right click – > Add –> new class file. Name this class file as TutorialStepDef.cs. Then save the project.
  • Open the TutorialStepDef.cs add following text in the top of code to existing “Using” code lines
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Support.UI;
  • Create a class name as TutorialStepDef as shown below
[Binding]
   public class StepDefinition1
   {    }
  • Inside this class we shall write a method that binds to call “Given I am on the Google home page”. If you look at Google.feature.cs file the testrunner call has the text “I am on the Google home page” and method name as Given. This method would look like this below with attribute as [Given]. The text that is passed to Given attribute should match with the one in testruuner call of Google.feature.cs file.
namespace SpecflowTutorial
{
    [Binding]
    class TutorialStepDef
    {
        [Given(@"I am on the Google home page")]
        public void GivenIAmOnTheGoogleHomePage()
        {
            driver.Navigate().GoToUrl("http://www.google.com");
            Assert.AreEqual("Google", driver.Title);
        }
    }
}
  • Similarly one has to write the code to When and Then steps. The final code in TutorialStepDef .cs file is as shown below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Support.UI;

namespace SpecflowTutorial
{
    [Binding]
    class TutorialStepDef
    {
        IWebDriver driver = new InternetExplorerDriver(@"C:\Data\seleniumCsharp\Drivers\");

        [Given(@"I am on the Google home page")]
        public void GivenIAmOnTheGoogleHomePage()
        {
            
            driver.Navigate().GoToUrl("http://www.google.com");
            Assert.AreEqual("Google", driver.Title);
        }

        [When(@"I search for text Webdriver")]
        public void WhenISearchForATextSelenium()
        {
            // Find the text input element by its name
            IWebElement query =driver.FindElement(By.Name("q"));
            // Input the search text
            query.SendKeys("Webdriver");
            // Now submit the form
            query.Submit();

            // Google's search is rendered dynamically with JavaScript.
            // Wait for the page to load, timeout after 5 seconds

            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
            IWebElement title = wait.Until<IWebElement>((d) =>
            {
                return d.FindElement(By.ClassName("ab_button"));
            });
        }

        [Then(@"I should see the search results with title WebDriver – Google Search")]
        public void ThenIShouldSeeTheSearchResultsForSelenium()
        {
            String title = "Webdriver - Google Search";
            //Check that the Title is what we are expecting
            Assert.AreEqual(title.ToString(), driver.Title);
        }
    }
}
  • Then next build the project and there should not be any build errors. Now how to run the test in nUnit.

3. Execute test in nUnit and generate specflow report.

nUnit should be installed in you machine and the Path variable of system should have nUnit bin directory. If this has been done, then in any new command window on typing nUnit and pressing enter should open up nUnit.  If this is not the case, you can refer my previous blog on nUnit
  • Lets try to run tests through commandline first. Open up a command window and navigate to bin directory of the project.  And then type nunit-console  SpecflowTutorial.dll  . This should start a IE browser and do the steps. Below output is seen in command window.
Runtime Environment -
   OS Version: Microsoft Windows NT 6.1.7601 Service Pack 1
  CLR Version: 2.0.50727.5472 ( Net 3.5 )


ProcessModel: Default    DomainUsage: Single
Execution Runtime: net-3.5
.Given I am on the Google home page
-> done: TutorialStepDef.GivenIAmOnTheGoogleHomePage() (6.3s)
When I search for text Webdriver
-> done: TutorialStepDef.WhenISearchForATextSelenium() (4.0s)
Then I should see the search results with title WebDriver - Google Search
-> done: TutorialStepDef.ThenIShouldSeeTheSearchResultsForSelenium() (0.0s)


Tests run: 1, Errors: 0, Failures: 0, Inconclusive: 0, Time: 11.2006407 seconds
  Not run: 0, Invalid: 0, Ignored: 0, Skipped: 0

  • Now you have successfully run the Specflow test. But the report from the command window is not sufficient.There is XML file generated by nUnit which can be formatted into specflow report. This where we are going to use second subcommand option of specflow – nunitexecutionreport. For this command one has pass the project file and the xml file as parameters. In command window navigate back to project directory where .csproj file is located. And then enter following command in command window
specflow.exe nunitexecutionreport SpecflowTutorial.csproj /xmlTestResult:.\bin\Debug\TestResult.xml

  • After this command is successfully executed , there is html file created in the project directory which is as shown in below snapshot.

image

4. Automate generation of nunit testrunner file having step defintion.

Thus so far you have created a sample scenario in Given,When and Then format and execute a test case in specflow plus generated report. But there were many steps that required you to hop in and out of Visual studio and command line. To avoid all these we can automate the generation of binding file from feature file and execution of nunit console/report generation using external tool feature visual studio express. Below step automates generation of *.feature.cs files.

  • Create a batch file say Generatesteps.bat and add the below content. And then save it.
echo Starting to generate SpecFlow unit tests
SpecFlow generateAll %1 /force /verbose
echo Finished generating SpecFlow unit tests
  • Open Tools –> External Tools and Click Add.
  • Give title as Generate Steps
  • In Command window, browse to the GenerateSteps.bat file and select it.
  • In Arguments type $(ProjectFileName)
  • In  Initial Directory type $(ProjectDir) and check option Use Output window.Click Apply and Ok
Now this external tool is setup and can be accessed from Tools –> Generate Steps. So instead of running a command line  like specflow generateall <ProjectPath>\<Projectname.csproj> /force /verbose , you can just click this tool and it automatically picks up all the feature files you created in the project directory and generate *.feature.cs files

5. Automate Execution of Specflow tests in nUnit and generation of report


  • Create a batch file and name it as RunSpecflowtests.bat. Add the below content to this batch file and save it

@echo off
nunit-console %1
if NOT %errorlevel% == 0 (
    echo "Error running tests - %errorlevel%"
    GOTO :exit
)
specflow.exe nunitexecutionreport %2 /xmlTestResult:%3
if NOT %errorlevel% == 0 (
    echo Error generating report - %errorlevel%
    GOTO :exit
)
if %errorlevel% == 0 TestResult.html
:exit

  • Go to Tools –> External tool and click Add
  • In Title give it as Run Specflow tests.
  • In Command, browse the batch you just created and select it.
  • In Arguements box, type “$(TargetName)$(TargetExt) $(ProjectDir)$(ProjectFileName) $(BinDir)TestResult.xml”.
  • In Initial Directory type “ $(BinDir)”. And select option Use Output window. Then click apply and Okay.
  • Once you have build the project instead of running command lines to execute nUnit-Console and specflow , you can just click Tools->Run Specflow tests and it should execute the tests and generate the Html file also.
In this test case, currently in the first method – Given, we are creating a driver. But best approach is create webdriver when feature gets initiated and close the browser when feature closes. I shall explain about this in further blogs.

---------------------------------------------

Related Posts Plugin for WordPress, Blogger...