Unit testing using NUnit in Visual Studio 2010

July 25, 2012 8 comments

What is Unit testing?

Unit testing is a method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine if they are fit for use. Intuitively, one can view a unit as the smallest testable part of an application. Let me take the example of the ‘Calculator’  for demonstrating the unit testing.

SOFTWARE REQUIREMENTS:

  • Windows 7 as OS
  • Microsoft Visual Studio 2010 as IDE
  • NUnit as Unit Testing Tool

SET-UP INSTRUCTIONS:

  • Create a new Project with C# Class Library and save it (‘Calculator’ in my example).
  • Rename the class ‘Class1.cs‘ to ‘Operator.cs‘.
  • Add the NUnit frameworks into the ‘Solution Explorer‘ by context-clicking on the project name and then select ‘Add Reference’ menu item.
  • When the Add Reference dialog appears, click on ‘Browse‘ and navigate to C:\Program Files (x86)\NUnit 2.5.10\bin\net-2.0\framework and select nunit.framework.dll.
  • Finally, the solution Explorer should look like this:

Create and writing scenarios for Unit tests:
  • Create scenarios for testing the operators such as Add, Subtract, Multiply, Divide, Square Root and Reciprocal.
  • Copy and paste the following code into the Operator.cs file in VS 2010.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
namespace Calculator
{
 public class Operator
 {

 public static double Add(double a, double b)
 {
 return a + b;
 }

 public static double Subtract(double a, double b)
 {
 return a - b;
 }

 public static double Multiply(double a, double b)
 {
 return a * b;
 }

 public static double Divide(double a, double b)
 {
 return a / b;
 }

 public static double SquareRoot(double a)
 {
 return Math.Sqrt(a);
 }

 public static double Reciprocal(double a)
 {
 return 1 / a;
 }

 [TestFixture]
 public class TestClass
 {

 [Test]
 public void AdditionTest()
 {
 double result = Operator.Add(2, 4);
 Assert.AreEqual(6, result);
 }

 [Test]
 public void SubtractionTest()
 {
 double result = Operator.Subtract(1, 3);
 Assert.AreEqual(-2, result);
 }

 [Test]
 public void MultiplicationTest()
 {
 double result = Operator.Multiply(4, 3);
 Assert.AreEqual(12, result);
 }

 [Test]
 public void DivisionTest()
 {
 double result = Operator.Divide(3, 0);
 Assert.AreEqual(0, result);
 }

 [Test]
 public void SquareRootTest()
 {
 double result = Operator.SquareRoot(-4);
 Assert.AreEqual(2, result);
 }

 [Test]
 public void ReciprocalTest()
 {
 double result = Operator.Reciprocal(0);
 Assert.AreEqual(0, result);
 }
 }
 }
}

—————-

Running the tests using NUnit:
  • In Visual Studio, Go to ‘Projects -> Project Properties’ (‘Calculator Properties’ in my example).
  • Click on the ‘Debug’ tab.
  • Set the ‘Start external program‘ to the location of NUnit exe file (C:\Program Files (x86)\NUnit 2.5.10\bin\net-2.0\nunit-x86.exe).
  • Build the solution, Go to ‘Build -> Build Solution‘ (hit the F6 key) in Visual Studio.
  • Execute the test, Go to ‘Debug -> Start Debugging’ (hit the F5 key) in Visual Studio. Visual Studio invokes the NUnit application.
  • In NUnit, click on ‘File -> Open Project‘ and choose the location of the Calculator.dll file as shown in the snapshot below.
  • In NUnit, click on the ‘Run‘ button to run the tests. Test is executed using NUnit and the result will be displayed in NUnit GUI window as shown in the snapshot below.

Only 3 tests are passed out of 6. Lets look into each of the failed tests – DivisionTest, ReciprocalTest and SquareRootTest.

Firstly, let us try with DivisionTest. When we divide 3/0, the calculator must display “Cannot divide by zero”. So, let us write an Exception error for Divide method.

public static double Divide(double a, double b)
{
 if (b == 0)
  {
  throw new InvalidOperationException("Cannot divide by zero");
  }
  return a / b;
}

Secondly, let us try with ReciprocalTest. When we reciprocate 0, the calculator must display “Cannot divide by zero”. So, let us write an Exception error for Reciprocal method.

public static double Reciprocal(double a)
{
if (a == 0)
{
throw new InvalidOperationException("Cannot divide by zero");
}
return 1 / a;
}

Thirdly, let us try with SquareRootTest. When we perform the square root operation for -4, the calculator must display “Invalid Input”. So, let us write an Exception error for SquareRoot method.

public static double SquareRoot(double a)
{
if (a < 0)
{
throw new InvalidOperationException("Invalid Input");
}
return Math.Sqrt(a);
}

Now, add valid numbers to each of the failed tests, build the code and run the test using NUnit. Final contents for the ‘Operator.cs‘ is as shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
namespace Calculator
{
 public class Operator
 {

 public static double Add(double a, double b)
 {
 return a + b;
 }

 public static double Subtract(double a, double b)
 {
 return a - b;
 }

 public static double Multiply(double a, double b)
 {
 return a * b;
 }

 public static double Divide(double a, double b)
 {
 if (b == 0)
   {
   throw new InvalidOperationException("Cannot divide by zero");
   }
   return a / b;
 }

 public static double SquareRoot(double a)
 {
 if (a < 0)
   {
   throw new InvalidOperationException("Invalid Input");
   }
   return Math.Sqrt(a);
 }

 public static double Reciprocal(double a)
 {
 if (a == 0)
   {
   throw new InvalidOperationException("Cannot divide by zero");
   }
   return 1 / a;
 }

 [TestFixture]
 public class TestClass
 {

 [Test]
 public void AdditionTest()
 {
 double result = Operator.Add(2, 4);
 Assert.AreEqual(6, result);
 }

 [Test]
 public void SubtractionTest()
 {
 double result = Operator.Subtract(1, 3);
 Assert.AreEqual(-2, result);
 }

 [Test]
 public void MultiplicationTest()
 {
 double result = Operator.Multiply(4, 3);
 Assert.AreEqual(12, result);
 }

 [Test]
 public void DivisionTest()
 {
 double result = Operator.Divide(4, 2);
 Assert.AreEqual(2, result);
 }

 [Test]
 public void SquareRootTest()
 {
 double result = Operator.SquareRoot(4);
 Assert.AreEqual(2, result);
 }

 [Test]
 public void ReciprocalTest()
 {
 double result = Operator.Reciprocal(2);
 Assert.AreEqual(0.5, result);
 }
 }
 }
}

Setup Jenkins on Windows and integrating it with NAnt

WHAT IS CONTINUOUS INTEGRATION?

Continuous integration (CI) implements continuous processes of applying quality control — small pieces of effort, applied frequently. Continuous integration aims to improve the quality of software, and to reduce the time taken to deliver it, by replacing the traditional practice of applying quality control after completing all development. I was interested in integrating the NAnt build tool with the Continuous Integration Tool and I choose Jenkins, which is an open source continuous integration tool written in Java.

PRINCIPLES OF CONTINUOUS INTEGRATION (Reference from Wikipedia):

  • Maintain a code repository: This practice advocates the use of a revision control system for the project’s source code. All artifacts required to build the project should be placed in the repository. In this practice and in the revision control community, the convention is that the system should be buildable from a fresh checkout and not require additional dependencies.
  • Automate the build: A single command should have the capability of building the system. Many build-tools, such as make, have existed for many years. Other more recent tools like Ant, Maven, MSBuild, OpenMake Meister or IBM Rational Build Forge are frequently used in continuous integration environments. Automation of the build should include automating the integration, which often includes deployment into a production-like environment.
  • Make the build self-testing: Once the code is built, all tests should run to confirm that it behaves as the developers expect it to behave.
  • Everyone commits to the baseline every day: By committing regularly, every committer can reduce the number of conflicting changes. Checking in a week’s worth of work runs the risk of conflicting with other features and can be very difficult to resolve. Early, small conflicts in an area of the system cause team members to communicate about the change they are making. Committing all changes at least once a day (once per feature built) is generally considered part of the definition of Continuous Integration. In addition performing a nightly build is generally recommended.
  • Every commit (to baseline) should be built: The system should build commits to the current working version in order to verify that they integrate correctly. A common practice is to use Automated Continuous Integration, although this may be done manually. For many, continuous integration is synonymous with using Automated Continuous Integration where a continuous integration server or daemon monitors the version control system for changes, then automatically runs the build process.
  • Keep the build fast: The build needs to complete rapidly, so that if there is a problem with integration, it is quickly identified.
  • Test in a clone of the production environment: Having a test environment can lead to failures in tested systems when they deploy in the production environment, because the production environment may differ from the test environment in a significant way. However, building a replica of a production environment is cost prohibitive. Instead, the pre-production environment should be built to be a scalable version of the actual production environment to both alleviate costs while maintaining technology stack composition and nuances.
  • Make it easy to get the latest deliverables: Making builds readily available to stakeholders and testers can reduce the amount of rework necessary when rebuilding a feature that doesn’t meet requirements. Additionally, early testing reduces the chances that defects survive until deployment. Finding errors earlier also, in some cases, reduces the amount of work necessary to resolve them.
  • Everyone can see the results of the latest build: It should be easy to find out whether the build breaks and, if so, who made the relevant change.
  • Automate deployment: Most CI systems allow the running of scripts after a build finishes. In most situations, it is possible to write a script to deploy the application to a live test server that everyone can look at. A further advance in this way of thinking is Continuous Deployment, which calls for the software to be deployed directly into production, often with additional automation to prevent defects or regressions.

SOFTWARE REQUIREMENTS:

  • Windows 7 as OS
  • Microsoft Visual Studio 2010 as IDE
  • NAnt as Build tool
  • NUnit as Unit Testing Tool
  • Selenium as a Test Automation Tool
  • Jenkins as Continuous Integration Tool
  • Java Installed (v1.7)
Java version Check:
  • Context-click ‘Computer‘, select ‘Properties‘ option.
  • Add the Java path in the ‘Path‘ System variables under ‘Advanced system settings ->Environment Variables‘.
  • Open the Command Prompt and type java -version and hit the ‘Enter’ key. Command Prompt will display following info:
 java version “1.7.0_05″
Java <TM> SE Runtime Environment <build 1.7.0_05-b05>
Java HotSpot<TM> Client VM <build 23.1-b03, mixed mode, sharing>
Jenkins Installation:
  • Download the Jenkins installer (jenkins.war) from the location: http://jenkins-ci.org/
  • Place the jenkins.war file in the location: C:\Development\Tools\Jenkins
  • Context-click ‘Computer‘, select ‘Properties‘ option. Then, Go to ‘Advanced system settings ->Environment Variables‘.
  • Add a new System variable by Variable name ‘JENKINS_HOME’ and set the Variable value to ‘C:\Development\Tools\Jenkins\.jenkins’
  • Open the Command Prompt and navigate to the location: C:\Development\Tools\Jenkins
  • Type java -jar jenkins.war and hit the ‘Enter‘ key. Jenkins is fully up and running. I have used the Environment Variable because when I run the command again, Jenkins will look for this Environment Variable and will automatically know to run.
  • All the Jenkins files will be extracted within the folder ‘.jenkins’ and will be saved in the location: ‘C:\Development\Tools\Jenkins\’ as shown below:
  • Launch a browser. Open the URL: http://localhost:8080/ and hit the ‘Enter‘ key.
  • Browser displays the Jenkins Home page as shown below:
Configuring NAnt with Jenkins:
  • Open the URL: http://localhost:8080/
  • Click on the ‘Manage Jenkins‘ link in the LHS of Jenkins Dashboard. It puts up the ‘Manage Jenkins‘ page as shown below:
  • Click on the ‘Manage Plugins‘ link. It puts up the ‘Plugin Manager‘ page.
  • Click on the ‘Available‘ tab, check the ‘NAnt Plugin‘ option under the ‘Build Tools‘ group and hit the ‘Download now and install after restart‘ button.
  • Restart the system and the NAnt plugin is installed.
  • Open the URL: http://localhost:8080/
  • Go to ‘Manage Jenkins’ -> ‘Manage Plugins’ -> ‘Installed‘ tab. NAnt plugin is listed in the ‘Installed‘ list as shown below:
  • Now, go to the Jenkins Dashboard screen and click on the ‘New Job‘ in the LHS links.
  • Add a job name (‘SampleSolution.Build‘ in my example), set the ‘Build a free-style software project checkbox and click on the ‘OK‘ button.
  • Under the ‘Build‘ section, click on the ‘Add build step‘ dropdown menu and select ‘Execute NAnt build‘.
  • I will be referencing my previous post, where I have integrated the NAnt Build with my Selenium Automation script. Add the location of NAnt: C:\Development\Work\SampleSolution\src\SampleSolution.Build\Nant.build in the ‘NAnt Build File’ field.
  • Click on the ‘Apply‘ button and then on the ‘Save’ button.
  • Go to the Jenkins Dashboard screen and you can see a new job added to the task list as shown below:
 
Running the Build task:
  • Hover the mouse on the new build task (i.e., SampleSolution.Build‘ in my case) and click on the ‘Build Now‘ option.
  • Build is executed, tests are executed in GUI mode and the build execution result will be displayed on the Jenkins Dashboard.

Integrating NAnt builder with Visual Studio to run the Selenium Web Driver tests using NUnit

June 29, 2012 25 comments

I was interested to run my Selenium tests using NAnt build tool as it can be integrated with the Continuous Integration tools such as Hudson. NAnt is a free and open source software tool for automating software build processes. It is similar to Apache Ant, but targeted at the .NET environment rather than Java.

REFERENCES:

This blog is referenced from the http://iainhunter.wordpress.com. Thanks to Iain Hunter for posting a great blog.

SOFTWARE REQUIREMENTS:

  • Windows 7 as OS
  • Microsoft Visual Studio 2010 as IDE
  • NAnt as Build tool
  • NUnit as Unit Testing Tool
  • Selenium as a Test Automation Tool
Workspace Structure:

It is always good to configure the workspace in a same way so that it is very easy to maintain the code. Here is the sample workspace:

Installing NAnt:
Add NAnt bin path to system path:
  • Context-click on ‘Computer’ -> ‘Properties -> Advanced System Settings’.
  • Click on the ‘Environment Variables‘ button.
  • Under the System Variables, edit the system variable ‘Path‘ and append ‘;C:\Development\Tools\nant-0.92-beta1\bin‘. Click on the ‘OK’ button as shown in the figure below.
  • Open the Powershell (i.e, Command Prompt). Navigate to ‘C:\Development\Tools\nant-0.92-beta1\bin’ and type nant. You should see a message below and ignore the failure message:
NAnt 0.92 (Build 0.92.4509.0; beta1; 6/05/2012)
Copyright (C) 2001-2012 Gerry Shaw

http://nant.sourceforge.net

Running NAnt from Visual Studio:
  • In Visual Studio, Go to the ‘Tools -> External Tools‘ menu.
  • Click on the ‘Add’ button in the ‘External Tools’ window.
  • Input a Title (say in my case ‘NAnt’).
  • Input the location of NAnt application in the ‘Command’ field (say in my case ‘C:\Development\Tools\nant-0.92-beta1\bin\NAnt.exe’).
  • Input the directory info in ‘Initial directory’ field (say in my case ‘$(ProjectDir)’).
  • Set the Use Output Option checkbox and hit the ‘Apply’ & OK’ buttons. A new option ‘NAnt’ under the ‘Tools’ menu.

Workspace Setup:
  • Create a new empty solution (say SampleSolution) and save it in the location: C:\Development\Work

  • Solution will be opened in Visual Studio containing the solution file.
  • Context-click or right-click the solution file and select ‘Add -> New Project‘.
  • Add projects such as SampleSolution.Web, SampleSolution.Tests and SampleSolution.Services as per .Net convention.
  • Set the location of the project to the /src folder within your solution.
  • Set the ‘No, do not create a unit test project‘ radio button in the ‘Create Unit Test Project‘ pop-up window.

  • Right-click on the solution file again and select Enable NuGet Package Restore option.
  • Click ‘Yes’ in the pop-up dialog asking the user to restore missing NuGet packages during build.
  • Now, the Solution Explorer will look like this:

  • Folder structure will look like this:
  • Test whether the NuGet is working by running the command:install-package TwitterBootstrap
  • Twitter Bootstrap will be installed successfully and solution directory is updated as shown below:
Installing NAnt.Builder:
  • Add a new empty project to the solution (say SampleSolution.Build) and save it in the \src  directory.

  • As we need to build the solution using the NAnt, we need to make some changes in the Build -> Configuration Manager settings.
  • In Visual Studio, Go to ‘Build -> Configuration Manager‘ and uncheck the ‘Build‘ checkbox for both the Active solution configurations – ‘Debug‘ and ‘Release‘ as shown below:

  • Install Nant.Builder from NuGet by running the following command from Package Manager Console: install-package nant.builder -projectname SampleSolution.Build
  • Nant.Builder is installed into your ‘SampleSolution.Build‘ project.
  • Solution Explorer is updated as shown below:

Configuring the Nant.build file:
  • Open the ‘Nant.build‘ file.
  • Set the ‘solution.name‘ property to ‘SampleSolution‘.
  • Set the ‘solution.projects‘ property to ‘SampleSolution.Services,SampleSolution.Tests,SampleSolution.Web‘.
  • Update the ‘version.tag‘ and ‘company.name‘ property as per your need.
  • Under ‘Nunit Test Settings‘, set the ‘run.nunit.tests‘ property to ‘true‘ and ‘tests.project.name‘ property to ‘SampleSolution.Tests‘.
<?xml version="1.0" encoding="utf-8" ?>

<project default="buildRelease" basedir=".">
 <include buildfile="GlobalBuildSettings.xml" />

<!--The name of your solution, please overwrite the default -->
 <property name="solution.name" value="SampleSolution"/>

<!-- Comma seperated list of projects contained in your solution.-->
 <property name="solution.projects" value="SampleSolution.Services,SampleSolution.Tests,SampleSolution.Web" />

<!-- Set the configuration for compilation, typically release, but may be custom -->
 <property name="release.configuration" value="Release" />

<!-- Manually set version, if using CCNet this will be overwritten later -->
 <property name="version.tag" value="1.0.0.0"/>
 <property name="company.name" value="anoopjshetty.wordpress.com" />

<!-- If your projects reside in a different directory from the .sln file specify here, or leave empty if not -->
 <property name="solution.src.dir" value="src" />

<!--Nunit Test Settings-->
 <property name="run.nunit.tests" value="true" />

<!--The name of the project containing your Nunit tests-->
<property name="tests.project.name" value="SampleSolution.Tests" />


Configuring the GlobalBuildSettings.xml file:
  • Open the ‘GlobalBuildSettings.xml‘ file.
  • Set the properties ‘local.buildspace‘, ‘local.workspace‘, ‘local.releasespace‘, ‘nunit-console.exe‘ accordingly as shown below:
<?xml version="1.0" encoding="utf-8" ?>
<!--Set up the properties appropriate for your local environment-->
<project name="GlobalBuildSettings">

 <!--Location where you want source built in your local workspace -->
 <property name="local.buildspace" value="C:\Development\Builds"/>
<!--Location where you want source built in your local workspace -->
 <property name="local.workspace" value="C:\Development\Work"/>

 <!--Location where you want source built in your local workspace -->
 <property name="local.releasespace" value="C:\Development\Releases"/>
<!--Location where Msbuild for .Net 4.0 is installed-->
 <property name="msbuild4.exe" value="C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe"/>

 <!--Location for Azure packager cspack-->
 <property name="cspack.exe" value="C:\Program Files\Windows Azure SDK\v1.6\bin\cspack.exe"/>

 <!--Location for Powershell-->
 <property name="powershell.exe" value="C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" />

 <!--Location for Nunit Console-->
 <property name="nunit-console.exe" value="C:\Development\Tools\NUnit 2.5.10\bin\net-2.0\nunit-console-x86.exe" />
</project>


Running the NAnt:
  • On the Command Prompt, Navigate to the location: C:\Development\Work\SampleSolution\src\SampleSolution.Build
  • Type ‘nant‘ and hit the ‘Enter‘ key.

BOOM…… BUILD SUCCEEDED

Adding and executing the Selenium WebDriver tests:
  • It’s the time to add the Selenium WebDriver tests to the SampleSolution.Tests project.
  • Context-click or right-click ‘References‘ of ‘SampleSolution.Tests‘ and select  ‘Add Reference‘ to add the Selenium and NUnit frameworks. References area should look like this after adding:
  • Copy and paste the following source code below to the UnitTest1.cs file on searching a text using the Firefox browser:
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
//using Microsoft.VisualStudio.TestTools.UnitTesting;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
namespace SampleSolution.Tests
{
 [TestFixture]
 public class UnitTest1
 {
 IWebDriver driver;

 [SetUp]
 public void Setup()
 {
 // Create a new instance of the Firefox driver
 driver = new FirefoxDriver();
 }

 [TearDown]
 public void Teardown()
 {
 driver.Quit();
 }

 [Test]
 public void GoogleSearch()
 {
 //Navigate to the site
 driver.Navigate().GoToUrl("http://www.google.com.au");
 // Find the text input element by its name
 IWebElement query = driver.FindElement(By.Name("q"));
 // Enter something to search for
 query.SendKeys("Selenium");
 // 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));
 wait.Until((d) => { return d.Title.StartsWith("selenium"); });
 //Check that the Title is what we are expecting
 Assert.AreEqual("selenium - Google Search", driver.Title);
 }
 }
}
  • On the Command Prompt, Navigate to the location: C:\Development\Work\SampleSolution\src\SampleSolution.Build
  • Type ‘nant‘ and hit the ‘Enter‘ key. Selenium tests are executed in the GUI mode and the test results are updated in the Command Prompt as shown below:

 
Running the same Tests from Visual Studio:
  • Context or right-click on the ‘SampleSolution.Build‘ in the ‘Solution Explorer‘ and select ‘Set as StartUp Project‘ option.
  • Go to ‘Project -> SampleSolution.Build Properties’.
  • Click on the ‘Debug‘ tab and set the ‘Start external program‘ location of Nunit.exe (i.e., C:\Development\Tools\NUnit 2.5.10\bin\net-2.0\nunit-x86.exe in my case)
  • Build and debug the project. All the tests will be executed in the GUI mode.

Test Automation Framework in Page-Object pattern using C#, Selenium, SpecFlow and NUnit

May 20, 2012 6 comments

Creating a Test Automation Framework is a challenging task due to following reasons:

  • Test framework design and the coding of that design requires significant time and effort
  • Test framework must be easy to expand and maintain
  • Test framework should be independent of application
  • Test design should remove most testers from the complexities of the test framework

Let me explain the creation of framework using the Page-Object pattern. Refer my previous blog for more info on this pattern.

SOFTWARE REQUIREMENTS:

  • Windows 7 as OS
  • Microsoft Visual Studio 2010 as IDE
  • SpecFlow as BDD tool for .Net
  • NUnit as Unit Testing Tool
  • Selenium as a Test Automation Tool

SET-UP INSTRUCTIONS:

Refer my previous blog on set-up instructions for creation and execution of automation tests using C#, Selenium, SpecFlow and NUnit.

Lets consider a test scenario:

Given I am on the Google home page
When I search for text selenium
Then I should see the search results

My task is to create a Page-Object pattern for the above test scenario. Contents in the files such as  GoogleSearch.feature and WebBrowser.cs are unchanged and I had defined the test actions using Selenium API within the GoogleSearchStepDefinition.cs file in my previous blog. Now, I need to apply the Page-Object pattern by creating a class for each web page. Create 2 class files as the test scenario has actions w.r.t 2 pages namely – GoogleHomePage.cs and GoogleSearchResultsPage.cs and this class file will be placed within a folder ‘Pages’ to separate from other class files. Now, I create methods within the GoogleSearchStepDefinition.cs file and these methods are executed within the page class GoogleHomePage.cs and GoogleSearchResultsPage.cs.

Copy and paste the code below to the  GoogleSearchStepDefinition.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
namespace SpecflowTest
{
 [Binding]
 public class StepDefinition1
 {
 [Given(@"I am on the Google home page")]
 public void GivenIAmOnTheGoogleHomePage()
  {
  var GoogleHP = new GoogleHomePage(WebBrowser.Current);
  GoogleHP.Navigate();
  }

 [When(@"I search for text (.*)")]
 public void WhenISearchForATextSelenium(string keyword)
  {
  var GoogleHP = new GoogleHomePage(WebBrowser.Current);
  GoogleHP.SearchOperation(keyword);
  }

 [Then(@"I should see the search results")]
 public void ThenIShouldSeeTheSearchResultsForSelenium()
  {
  var GoogleSRP = new GoogleSearchResultsPage(WebBrowser.Current);
  GoogleSRP.ValidateTest();
  }
 }
}

Copy and paste the code below to the  GoogleHomePage.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;
namespace SpecflowTest
{
 class GoogleHomePage
 {
 private IWebDriver driver;

 //Contructor to re-use the driver
 public GoogleHomePage(IWebDriver driver)
 {
 this.driver = WebBrowser.Current;
 }

 public void Navigate()
 {
 //Navigate to the site
 WebBrowser.Current.Navigate().GoToUrl("http://www.google.com");
 //Check that the Title is what we are expecting
 Assert.AreEqual("Google", WebBrowser.Current.Title);
 }

 public void SearchOperation(string keyword)
 {
 // Find the text input element by its name
 IWebElement query = WebBrowser.Current.FindElement(By.Name("q"));
 // Input the search text
 query.SendKeys(keyword);
 // 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(WebBrowser.Current, TimeSpan.FromSeconds(5));
 IWebElement title = wait.Until<IWebElement>((d) =>
 {
  return d.FindElement(By.ClassName("ab_button"));
 });
 }
 }
}

Copy and paste the code below to the  GoogleSearchResultsPage.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;
namespace SpecflowTest
{
 class GoogleSearchResultsPage
 {
 private IWebDriver driver;

 //Contructor to re-use the driver
 public GoogleSearchResultsPage(IWebDriver driver)
 {
 this.driver = WebBrowser.Current;
 }

 public void ValidateTest()
 {
 //Check that the Title is what we are expecting
 Assert.AreEqual("selenium - Google Search", WebBrowser.Current.Title);
 }
 }
}

Run the tests using NUnit and the tests will Pass. Now, we are almost 90% done with our task in the creation of a test framework in Page-Object pattern but we need to re-factor this pattern by creating a Base Page class. The need for the base class is to remove the redundant methods. In the GoogleHomePage.cs and GoogleSearchResultsPage.cs file, there is a redundant method on initializing the driver:

private IWebDriver driver;

 //Contructor to re-use the driver
 public GoogleSearchResultsPage(IWebDriver driver)
 {
 this.driver = WebBrowser.Current;
 }

So, create a Base Page class say BasePage.cs and move the above method on initialising the driver to the Base Page Class as shown below:

private IWebDriver driver;

 protected BasePage(IWebDriver driver, String loadUrl = "")
 {
 this.driver = WebBrowser.Current;
 WebUrl = loadUrl;
 }

Similarly, I have added a method on navigating to the webpage to the BasePage.cs as shown below:

private readonly string WebUrl;

public void GoToSite()
 {
 driver.Navigate().GoToUrl(WebUrl);
 }
 
 
Final contents of GoogleHomePage.cs as shown below:
             
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;
namespace SpecflowTest
{
 class GoogleHomePage : BasePage
 {
 private static readonly String Url = "http://www.google.com";
//Contructor to re-use the driver
 public GoogleHomePage(IWebDriver driver) : base(driver, Url)
 {
 }

 public void Visit(string Url)
 {
 GoToSite();
 //Check that the Title is what we are expecting
 Assert.AreEqual("Google", WebBrowser.Current.Title);
 }

 public void SearchOperation(string keyword)
 {
 // Find the text input element by its name
 IWebElement query = WebBrowser.Current.FindElement(By.Name("q"));
 // Input the search text
 query.SendKeys(keyword);
 // 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(WebBrowser.Current, TimeSpan.FromSeconds(5));
 IWebElement title = wait.Until<IWebElement>((d) =>
 {
 return d.FindElement(By.ClassName("ab_button"));
 });
 }
 }
}
      
    
Final contents of GoogleSearchResultsPage.cs as shown below:
      

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;

namespace SpecflowTest
{
 class GoogleSearchResultsPage : BasePage
 {
 //Contructor to re-use the driver
 public GoogleSearchResultsPage(IWebDriver driver) : base(driver)
 {
 }

 public void ValidateTest()
 {
 //Check that the Title is what we are expecting
 Assert.AreEqual("selenium - Google Search", WebBrowser.Current.Title);
 }
 }
}

 
 
Final contents of BasePage.cs as shown below:
     
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System.Threading;
using SpecflowTest;
namespace SpecflowTest
{
 class BasePage
 {
 private IWebDriver driver;
 private readonly string WebUrl;

 protected BasePage(IWebDriver driver, String loadUrl = "")
 {
 this.driver = WebBrowser.Current;
 WebUrl = loadUrl;
 }

 public void GoToSite()
 {
 driver.Navigate().GoToUrl(WebUrl);
 }
}
}
 
 
Run the test using NUnit:

 

Page-Object pattern for creating a Test Automation Framework

May 12, 2012 7 comments

Page-Object pattern is one of the most effective approaches for creating a Test Automation Framework. This pattern maps a GUI page to a page class and the functionality to interact about the page is captured within the Page class (i.e., a new page class created for each new web page). So, you are calling the page class methods that perform action on the web page in your tests.

Test maintenance becomes easier by creating a Base Page class as it contains most common functions that need to be performed on any web page. For example: If need to navigate to a webpage in many of my tests, I may define this in my Base Page Class and then my Page Class must inherit this.

Advantages of using Page-Object pattern in Selenium:
  • Test code is easily readable.
  • Distinguished reusable code.
  • No duplication of Selenium API calls.
  • Creation of tests are easy and improves maintainability.
  • Used effectively with IDE such as Visual Studio, Eclipse, IntelliJ, Visual Studio.
  • Excellent support for languages such as C#, Java, Ruby, Python, Perl.

Test Execution Report in SpecFlow

April 27, 2012 5 comments

It would be very good to generate test reports related to Acceptance tests for satisfying our requirements and providing the test statuses to Customers & Project Managers. This report provides a formatted HTML report of a test execution. The report contains a summary about the executed tests and the result and also a detailed report for the individual scenario executions.

NUnit Test Execution Report

Let me create a Test Execution Report for my previous blog on SpecFlow. In order to generate this report you have to execute the acceptance tests with the nunit-console runner. This tool generates an XML summary about the test executions. To have the detailed scenario execution traces visible, you also need to capture the test output using the /out and the /labels options as it can be seen in the following example.

Step 1:

nunit-console.exe /labels /out=TestResult.txt /xml=TestResult.xml bin\Debug\SpecflowTest.dll

The two generated file can be used to invoke the SpecFlow report generation. If you use the output file names shown above it is enough to specify the project file path containing the feature files.

Step 2:

specflow.exe nunitexecutionreport SpecflowTest.csproj /out:MyResult.html

Step 1 is already executed as we are running the tests using the NUnit GUI and this automatically generates a TestResult.xml file in the bin\Debug folder as shown in the snapshot below:

 

Now, let me jump to the Step 2. Step 2 can be executed by using the External tools in the Visual Studio.

  • Go to Tools -> External Tools
  • Click on the ‘Add’ button in the ‘External Tools’ window
  • Input a Title (say in my case ‘SpecFlow Test Result’)
  • Input the location of SpecFlow application in the ‘Command’ field (say in my case ‘C:\Program Files (x86)\TechTalk\SpecFlow\specflow.exe’)
  • Input the argument in the ‘Arguments’ field (say in my case ‘nunitexecutionreport SpecflowTest.csproj /xmlTestResult:bin\debug\TestResult.xml’)
  • Input the directory info in ‘Initial directory’ field (say in my case ‘$(ProjectDir)’)
  • Set the ‘Close on exit’ checkbox and hit the ‘Apply’ & ‘OK’ buttons. A new option ‘SpecFlow Test Result’ under the ‘Tools’ menu.
 
Generating the Test Report
  • Debug the project and run the test using NUnit GUI window.
  • Click on the option ‘SpecFlow Test Result’ under the ‘Tools’ menu.
  • Open the ‘TestResult.html’ file present in the ‘Projects’ folder using the browser as in the snapshot below.
  • Create a short-cut to the ‘TestResult.html’ file by adding the file to the Visual Studio Solution Explorer for quick viewing as in the snapshot below.
 

Behaviour Driven Development (BDD) in .Net using SpecFlow, Selenium and NUnit

April 19, 2012 18 comments

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

SOFTWARE REQUIREMENTS:

  • Windows 7 as OS
  • Microsoft Visual Studio 2010 as IDE
  • SpecFlow as BDD tool for .Net
  • NUnit as Unit Testing Tool
  • Selenium as a Test Automation Tool

SET-UP INSTRUCTIONS:

To install the Selenium WebDriver project:
  • Create a new Project with C# Class Library and save it (‘SpecflowTest‘ in my example).
  • Download and install NuGet Package Manager using Tools -> Extension Manager. Restart MS Visual Studio in order for the changes to take effect.
  • Go to Package Manager Console install the latest version of Selenium WebDriver by running the command Install-Package Selenium.WebDriver -Version 2.20.0
  • Install the latest version of Selenium WebDriver Support Classes by running the command Install-Package Selenium.Support
 
Referencing the NUnit Library:
  • Download the latest NUnit framework from the site: http://www.nunit.org
  • Install the NUnit software on your machine.
  • In Visual Studio, Go to the Project -> Add Reference menu item.
  • When the Add Reference dialog appears, click on ‘Browse‘ and navigate to C:\Program Files (x86)\NUnit 2.5.10\bin\net-2.0\framework and select nunit.framework.dll.
Referencing the SpecFlow:
  • Download the latest NUnit framework from the site: http://www.specflow.org/
  • Install the SpecFlow software on your machine.
  • In Visual Studio, Go to the Project -> Add Reference menu item.
  • When the Add Reference dialog appears, click on ‘Browse‘ and navigate to C:\Program Files (x86)\TechTalk\SpecFlow and select TechTalk.SpecFlow.dll.
  • Finally, the solution Explorer should look like this:

Consider a sample user story:

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

Let me write a test scenario for the above story:

Given I am on the Google home page
When I search for text selenium
Then I should see the search results

Creating a new SpecFlow feature file:
  • Context-click on the project name ‘SpecflowTest‘ in the Solution Explorer
  • Select Add -> New Item
  • Select the ‘SpecFlow Feature File‘ and save it as ‘GoogleSearch.feature
 
 
 
 
 
  •  Specflow creates a new file “GoogleSearch.feature” and a designer file “GoogleSearch.feature.cs“. The default content of the Specflow file is in the Gherkin format.
  • 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
  • Change the contents of the file to the new scenario as below and the Specflow generates a feature file ‘GoogleSearch.feature.cs':
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

@mytag
Scenario: Google Search
 Given I am on the Google home page
 When I search for text selenium
 Then I should see the search results

Creating a new SpecFlow Step Definition file:
  • Rename the class ‘Class1.cs‘ to ‘GoogleSearchStepDefinition.cs‘.
  • Context-click on the feature file ‘GoogleSearch.feature‘ and select …… System generates a specification file ‘…..’ as shown below.
  • Debug the project. Go to Projects -> Project Properties (‘SpecflowTest Properties‘ in my example).
  • Click on the ‘Debug‘ tab and set the ‘Start external program‘ to the location of NUnit exe file (C:\Program Files (x86)\NUnit 2.5.10\bin\net-2.0\nunit-x86.exe).
  • Build the solution, Go to ‘Build -> Build Solution‘ (hit the F6 key) in Visual Studio.
  • Execute the test, Go to ‘Debug -> Start Debugging‘ (hit the F5 key) in Visual Studio. Visual Studio invokes the NUnit application.
  • In NUnit, click on ‘File -> Open Project‘ and choose the location of the SpecflowTest.dll file.
  • In NUnit, click on the ‘Run‘ button to run the tests. Test shows Inconclusive result and the result is displayed in NUnit ‘Text Output’ tab.
  • Copy the contents of the NUnit  ‘Text Output‘ tab and paste it in the ‘GoogleSearchStepDefinition.cs‘ step definition file as shown below. The key point here is that the class needs to be annotated with the binding attribute and we can use the (.*) expression to represent variables coming from the feature file.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
namespace SpecflowTest
{
 [Binding]
 class GoogleSearchStepDefinition
  {

  [Given(@"I am on the Google home page")]
  public void GivenIAmOnTheGoogleHomePage()
  {
  ScenarioContext.Current.Pending();
  }

  [When(@"I search for text (.*)")]
  public void WhenISearchForATextSelenium()
  {
  ScenarioContext.Current.Pending();
  }

  [Then(@"I should see the search results")]
  public void ThenIShouldSeeTheSearchResultsForSelenium()
  {
  ScenarioContext.Current.Pending();
  }
}
}
 
Creating a Static Browser class for defining the browser selection:
  • This class is a static helper class for selecting the browser and Selenium has support for Internet Explorer, Firefox and Chrome.
  • Context-click on the project name ‘SpecflowTest‘ in the Solution Explorer and select Add -> New Item
  • Create a C# file with name ‘WebBrowser.cs‘.
  • Now, copy and paste the code below to the ‘WebBrowser.cs‘.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Chrome;
namespace SpecflowTest
{
   [Binding]
   public class WebBrowser
   {
     public static IWebDriver Current
     {
       get
       {
          if (!ScenarioContext.Current.ContainsKey("browser"))
          {
          //Select IE browser
          ScenarioContext.Current["browser"] = new InternetExplorerDriver();

          //Select Firefox browser
          //ScenarioContext.Current["browser"] = new FirefoxDriver();

          //Select Chrome browser
          //ScenarioContext.Current["browser"] = new ChromeDriver();
          }
        return (IWebDriver)ScenarioContext.Current["browser"];
        }
      }

    [AfterScenario]
    public static void Close()
    {
     if (ScenarioContext.Current.ContainsKey("browser"))
      {
          Current.Dispose();
      }
    }
  }
}

Implementing the Steps:
  • Remove all the methods ‘ScenarioContext.Current.Pending()
  • Now, copy and paste the code below to the ‘GoogleSearchStepDefinition.cs‘ as shown below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
namespace SpecflowTest
{
 [Binding]
 public class StepDefinition1
 {

 [Given(@"I am on the Google home page")]
 public void GivenIAmOnTheGoogleHomePage()
 {
 //Navigate to the site
 WebBrowser.Current.Navigate().GoToUrl("http://www.google.com");
 //Check that the Title is what we are expecting
 Assert.AreEqual("Google", WebBrowser.Current.Title);
 }

 [When(@"I search for text (.*)")]
 public void WhenISearchForATextSelenium(string keyword)
 {
 // Find the text input element by its name
 IWebElement query = WebBrowser.Current.FindElement(By.Name("q"));
 // Input the search text
 query.SendKeys(keyword);
 // 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(WebBrowser.Current, TimeSpan.FromSeconds(5));
  IWebElement title = wait.Until<IWebElement>((d) =>
  {
  return d.FindElement(By.ClassName("ab_button"));
  });
 }

 [Then(@"I should see the search results")]
 public void ThenIShouldSeeTheSearchResultsForSelenium()
 {
 //Check that the Title is what we are expecting
 Assert.AreEqual("selenium - Google Search", WebBrowser.Current.Title);
 }

}
}
 
Executing the Tests:
  • Build the solution, Go to ‘Build -> Build Solution‘ (hit the F6 key) in Visual Studio.
  • Execute the test, Go to ‘Debug -> Start Debugging‘ (hit the F5 key) in Visual Studio. Visual Studio invokes the NUnit application.
  • In NUnit, click on ‘File -> Open Project‘ and choose the location of the SpecflowTest.dll file.
  • In NUnit, click on the ‘Run‘ button to run the tests. Test is executed using NUnit and the result will be displayed in NUnit GUI window as shown in the snapshot below.
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: