Multi-Browser Testing an MVC Application With Selenium and NUnit

1 Jun 2013

I been doing a lot more work in application testing recently, and automated testing of a web application UI is something I have yet to play with until now. I believe there is no reason not to do automated testing, the benefits are too good to not follow through with; my experience in small and large teams has shown the dangers and problems with not doing testing.

The example below is a simple NUnit test that uses the test case functionality of NUnit to run against multiple browsers. The sample web-application is MVC but it doesn't really matter for the test case itself. The only important part is that you define what on the page is static and what is dynamic so the test case can do correct asserts of the page.

I am using Selenium for the browser automation testing because of its wide support, with companies such as Google using it for thousands of tests. Directly quoted from the Google Testing blog:

Selenium (and Webdriver) are used very heavily at Google, and we have a centralized farm of Selenium machines to execute these tests around the clock."

There are other options such as WatiN and Coded UI. I have not done more than some research into these, but my understanding is that WatiN has less support, and Coded UI is newer and has been more aimed at Windows UI, rather than Web until more recently.

The below is a very basic use of Selenium and I will blog more advanced cases as I use them, they have support for more browsers including mobile testing but the WebDriver example below is still quite new.

For verifying correct behaviour I decided to create a simple MVC helper that will output a hidden form field with certain values so that Selenium can use this to verify functionality. I decided upon a hidden form field because a hidden span element (for example) can't be found simply with Selenium due to it testing what is actually rendered to the screen. I have added in the check so this only outputs if in debug mode, but if the data will be fine to show on live then that could be removed and then the tests may be able to run against live as a verification of the release.

namespace makit.Shneee.Web.Helpers
{
    using System.Web.Mvc;
    using System.Web.Mvc.Html;

    public static class WebTestingExtensions
    {
        public static MvcHtmlString WebTestingElement(this HtmlHelper helper, string id)
        {
            return WebTestingElement(helper, id, string.Empty);
        }

        public static MvcHtmlString WebTestingElement(this HtmlHelper helper, string id, string value)
        {
            var commentValue = string.Empty;

#if (DEBUG)
            commentValue = string.Format("", 
                id,
                helper.ViewContext.HttpContext.Request.Url.PathAndQuery,
                value);
#endif

            return MvcHtmlString.Create(commentValue);
        }
    }
}

This can then be used by added some view code (using Razor) such as:

@Html.WebTestingElement("index", "static")

This will render out a tag such as below for the default root page:

<input type="hidden" name="wt_index" value="[/][static]" />

This allows any number of tags as long as they are given unique names, and the element can be output within razor logic so a tag could show only if logged in, one showing the username, etc. The assert can then check the user is logged in correctly by the presence of this tag.

Now the actual test, I used NuGet on the testing library to add NUnit, Selenium WebDriver and Selenium WebDriver Support Classes.

namespace makit.Shneee.Tests.Web
{
    using System;
    using NUnit.Framework;
    using OpenQA.Selenium;
    using OpenQA.Selenium.Chrome;
    using OpenQA.Selenium.Firefox;
    using OpenQA.Selenium.IE;
    using OpenQA.Selenium.Safari;

    [TestFixture]
    public class HomePageTests
    {
        #region Constants

        public static string SiteRootUrl = "http://localhost:64151";

        public const int DriverTimeOut = 30;

        public const string ExpectedPageTitle = "Home Page - Shneee Software Development Manager";

        #endregion

        #region Tests

        [TestCase(Browser.Chrome)]
        [TestCase(Browser.Firefox)]
        public void GivenBrowser_WhenLaunchDefaultUrl_ReturnsHomePage(Browser browser)
        {
            const string fieldName = "wt_index";
            const string fieldValueShouldBe = "[/][static]";
            
            using(var driver = GetWebDriverForBrowser(browser))
            {
                driver.Navigate().GoToUrl(SiteRootUrl);

                Assert.That(driver.Title, Is.EqualTo(ExpectedPageTitle));

                var elementValue = driver.FindElement(By.Name(fieldName)).GetAttribute("value");
                Assert.That(elementValue.Equals(fieldValueShouldBe), Is.True, string.Format("Failed due to value being [{0}]", elementValue));
            }
        }

        #endregion

        #region Helpers

        public enum Browser
        {
            Chrome,
            Firefox,
            IE,
            Safari
        }

        private IWebDriver GetWebDriverForBrowser(Browser browser)
        {
            IWebDriver driver = null;

            switch (browser)
            {
                case Browser.Chrome:
                    driver = new ChromeDriver();
                    break;
                
                case Browser.Firefox:
                    driver = new FirefoxDriver();
                    break;

                case Browser.IE:
                    driver = new InternetExplorerDriver();
                    break;

                case Browser.Safari:
                    driver = new SafariDriver();
                    break;
            }

            if (driver != null)
            {
                driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(DriverTimeOut));
            }

            return driver;
        }

        #endregion
    }
}

The code should be fairly self explanatory, first it takes in which browser to test against as a NUnit test case. This allows cross browser checks of functionality at the down-side of slower executing tests. The code then gets the correct web driver for the browser chosen using a simple function and sets a timeout value. The driver is then told to execute a URL which I have as a constant for simplicity. A check is done to make sure the page title is correct and then the driver is told to find the hidden field element and assert the value of it against what is expected.

On running via NUnit the browsers will open and then close after the test has run, in total it takes 10 seconds to run over two browsers.

NUnit Run of Selenium Tests
Posted by makit
Last revised 22 Jan 2017 11:25 AM

makit / Martyn Kilbryde

Professional software developer, ponderer and eccentric.

flickr

Github

LinkedIn

Stack Overflow

Twitter