NUnit, Selenium WebDriver multiple Test Cases and Browsers
- Download and install NUnit.
- Install NUnit Test Adapter (Tools \ Extensions Manager...)
- Create Unit Test Project
- Install (Tools \ Library Package Manager \ Manage NuGet Packages for Solution...):
- Selenium WebDriver
- Selenium WebDriver Support Classes
- NUnit
Here is example code that runs on multiple browsers with multiple test cases:
using System;
using System.Text.RegularExpressions;
using System.Threading;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
namespace Example
{
[TestFixture(typeof(FirefoxDriver))]
[TestFixture(typeof(InternetExplorerDriver))]
[TestFixture(typeof(ChromeDriver))]
public class TestWithMultipleBrowsers<TWebDriver> where TWebDriver : IWebDriver, new()
{
#region Setup
private IWebDriver driver;
[TestFixtureSetUp]
public void CreateDriver()
{
if (typeof(TWebDriver).Name == "ChromeDriver")
{
// http://code.google.com/p/chromedriver/downloads/list
driver = new ChromeDriver("C:\\chromedriver");
}
else
{
driver = new TWebDriver();
}
}
[TearDown]
public void TearDown()
{
// Take screen on failure
if (TestContext.CurrentContext.Result.Status == TestStatus.Failed)
{
string fileName = Regex.Replace(TestContext.CurrentContext.Test.FullName, "[^a-z0-9\\-_]+", "_", RegexOptions.IgnoreCase);
((ITakesScreenshot)driver).GetScreenshot().SaveAsFile("C:\\Users\\AlexandrM\\Desktop\\" + fileName + ".png", System.Drawing.Imaging.ImageFormat.Png);
}
}
[TestFixtureTearDown]
public void FixtureTearDown()
{
if (driver != null) driver.Quit();
}
#endregion
#region Tests
[Test]
[Description("This test will always fail")]
public void FailTest()
{
driver.Navigate().GoToUrl("http://www.google.com/");
Thread.Sleep(1000);
Assert.IsTrue(driver.Title.Contains("rabota.ua"));
}
[Test]
[Description("Test google search")]
[Category("google"), Category("search")]
[TestCase("jobsearch")]
[TestCase("employer")]
public void GoogleTest(string search)
{
driver.Navigate().GoToUrl("http://www.google.com/");
IWebElement query = driver.FindElement(By.Name("q"));
query.SendKeys(search + Keys.Enter);
Thread.Sleep(1000);
Assert.AreEqual(search + " - Поиск в Google", driver.Title);
}
#endregion
}
}
Also you can run tests via console, like this:
"C:\Program Files (x86)\NUnit 2.6\bin\nunit-console" "C:\Users\AlexandrM\Documents\Visual Studio 11\Projects\Example\Example\Example.csproj" /result:"C:\Users\AlexandrM\Desktop\result.xml"
Notice that result will be saved as XML file, which can be transformed with old Summary.xslt by adding
<?xml-stylesheet type="text/xsl" href="Summary.xslt"?>
right after xml declaration or you can write your own xslt file.
Here is code from nunit.xslt (can not find link where found it):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method='html' indent='yes'/>
<xsl:template match="/">
<html>
<xsl:apply-templates/>
</html>
</xsl:template>
<xsl:template match="test-results">
<head>
<script type="text/javascript">
function ShowHideDetails(contentControlId) {
var contentControl = document.getElementById(contentControlId);
if(contentControl) {
if(contentControl.style.display == 'none') {
contentControl.style.display = '';
} else {
contentControl.style.display = 'none';
}
}
}
</script>
</head>
<body>
<div id="uxHeader" style="font-weight:bold;">
Tests run: <xsl:value-of select="@total"/>, Failures: <xsl:value-of select="@failures"/>, Not run: <xsl:value-of select="@not-run"/>, Time: <xsl:value-of select="test-suite/@time"/> seconds
</div>
<xsl:if test="//test-case[@success = 'False']">
<div style="font-weight:bold;margin-top:10px;border-top:1px solid black;font-size:14pt;">
Failed Tests
</div>
<xsl:for-each select="//test-suite">
<xsl:if test="results/test-case[@success = 'False']">
<div style="margin-top:5px;font-weight:bold;"><xsl:value-of select="@name" /></div>
<div style="margin-left:10px;">
<xsl:call-template name="failureTemplate"></xsl:call-template>
</div>
</xsl:if>
</xsl:for-each>
</xsl:if>
<xsl:if test="//test-case[@executed = 'False']">
<div style="font-weight:bold;margin-top:10px;border-top:1px solid black;font-size:14pt;">
Ignored Tests
</div>
<xsl:for-each select="//test-suite">
<xsl:if test="results/test-case[@executed = 'False']">
<div style="margin-top:5px;font-weight:bold;"><xsl:value-of select="@name" /></div>
<div style="margin-left:10px;">
<xsl:call-template name="ignoreTemplate"></xsl:call-template>
</div>
</xsl:if>
</xsl:for-each>
</xsl:if>
<div style="font-weight:bold;margin-top:10px;border-top:1px solid black;font-size:14pt;">
Successful Tests
</div>
<xsl:for-each select="//test-suite">
<xsl:if test="results/test-case[@success = 'True']">
<div style="margin-top:5px;font-weight:bold;"><xsl:value-of select="@name" /></div>
<div style="margin-left:10px;">
<xsl:call-template name="passTemplate"></xsl:call-template>
</div>
</xsl:if>
</xsl:for-each>
</body>
</xsl:template>
<xsl:template match="results/test-case[failure]" name="failureTemplate">
<xsl:for-each select="results/test-case[@success = 'False']">
<span style="font-weight:bold;font-size:12pt;">
<xsl:value-of select="position()"/>)
</span>
<xsl:value-of select="@name"/>
<div style="font-size:10pt;margin-left:25px;">Reason:
<xsl:choose><xsl:when test="string-length(child::node()/message)=0"> [not defined]</xsl:when>
<xsl:otherwise> "<xsl:value-of select="child::node()/message"/>"</xsl:otherwise>
</xsl:choose>
</div>
</xsl:for-each>
</xsl:template>
<xsl:template match="results/test-case[reason]" name="ignoreTemplate">
<xsl:for-each select="results/test-case[@executed = 'False']">
<span style="font-weight:bold;font-size:12pt;">
<xsl:value-of select="position()"/>)
</span>
<xsl:value-of select="@name"/>
<div style="font-size:10pt;margin-left:25px;">Reason:
<xsl:choose><xsl:when test="string-length(child::node()/message)=0"> Ignored </xsl:when>
<xsl:otherwise> "<xsl:value-of select="child::node()/message"/>"</xsl:otherwise>
</xsl:choose>
</div>
</xsl:for-each>
</xsl:template>
<xsl:template match="results[test-case]" name="passTemplate">
<xsl:for-each select="results/test-case[@success = 'True']">
<span style="font-weight:bold;font-size:12pt;">
<xsl:value-of select="position()"/>)
</span>
<xsl:value-of select="@name"/>
<div style="font-size:10pt;margin-left:25px;">Reported: Success
</div>
<xsl:text></xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="Newline"><xsl:text>
</xsl:text></xsl:template>
</xsl:stylesheet>
Here is some screen shots, tests also can be runned from VisualStudio and NUnit GUI.
As of making nice reports, here is starter kit:
<?xml version="1.0"?>
<!-- http://www.bizcoder.com/index.php/2010/02/12/convert-xml-to-json-using-xslt/ -->
<!-- http://xmlplease.com/whitespace -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<meta charset="UTF-8" />
<title>Test Results</title>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css" />
<!--[if lt IE 9]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
</head>
<body>
<div data-bind="with: test_results">
<p>Date: <span data-bind="text: date"></span></p>
<p>Tests: <span data-bind="text: success"></span> of <span data-bind="text: total"></span></p>
</div>
<script type="text/javascript" src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.js"></script>
<script type="text/javascript" src="https://raw.github.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.js"></script>
<script type="text/javascript">
var data ={<xsl:apply-templates select="*"/>};
data.test_results.success = parseInt(data.test_results.total) - parseInt(data.test_results.errors) - parseInt(data.test_results.failures) - parseInt(data.test_results.ignored) - parseInt(data.test_results.inconclusive) - parseInt(data.test_results.invalid) - parseInt(data.test_results.not_run) - parseInt(data.test_results.skipped);
//TODO: do something with data here
var results = ko.mapping.fromJS(data);
ko.applyBindings(results);
</script>
</body>
</html>
</xsl:template>
<!-- Object or Element Property-->
<xsl:template match="*">
"<xsl:value-of select="translate(name(), '-', '_')"/>" : <xsl:call-template name="Properties"/>
</xsl:template>
<!-- Array Element -->
<xsl:template match="*" mode="ArrayElement">
<xsl:call-template name="Properties"/>
</xsl:template>
<!-- Object Properties -->
<xsl:template name="Properties">
<xsl:variable name="childName" select="name(*[1])"/>
<xsl:choose>
<xsl:when test="not(*|@*)">"<xsl:value-of select="normalize-space(translate(., ' 	
', ' '))"/>"</xsl:when>
<xsl:when test="count(*[name()=$childName]) > 1">{ "<xsl:value-of select="translate($childName, '-', '_')"/>" :[<xsl:apply-templates select="*" mode="ArrayElement"/>] }</xsl:when>
<xsl:otherwise>{
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="*"/>
}</xsl:otherwise>
</xsl:choose>
<xsl:if test="following-sibling::*">,</xsl:if>
</xsl:template>
<!-- Attribute Property -->
<xsl:template match="@*">"<xsl:value-of select="translate(name(), '-', '_')"/>" : "<xsl:value-of select="translate(., '"', '\')"/>",
</xsl:template>
</xsl:stylesheet>
The main idea is that all xml is converted to knockout json model, so you can make interactive report.