Automation, C#, NUnit, Selenium

How to Automate Responsive Applications – Cross Browser Testing (Selenium, C#, NUnit)

Posted by Kerry

DIFFICULTY: Intermediate

In a world where websites are expected to work across multiple browsers, it’s no wonder why cross-browser testing is such a hot ticket item. When you consider all the different browser types and all of your tests you need to execute, it can quickly become cumbersome.

And then there’s responsiveness.

With a responsive app, you need to account not only for browser types, but also viewport sizes across those different browser types. Then factor in different device types, and possibly different browser versions, and… Congratulations! You have exponentially increased your workload, and probably have a stress headache.

Executing it all manually is not only generally infeasible with time constraints, but it’s also an excellent way to miss defects.

So let’s automate it!

In this tutorial, I will show you how to automate a responsive application across different browsers (and sizes) on desktop, tablet, and phone devices.

Step 1: Clone the Repository

Typically I include the GitHub repository for the Java solution in addition to the C# walkthrough. Given the difference in implementation between Java/JUnit and C#/NUnit, we’ll cover the Java implementation in the next post.

Step 2: Create Our Enums

We are going to pass along two parameters for each test to determine the browser (Chrome, Firefox, Edge) and device type (Desktop, Tablet, Phone).

In order to do this, we will first create two enum classes called…you guessed it…Brower and Device.

public enum Browser
{
    Chrome,
    Firefox,
    Edge,
    Phone
}
public enum Device
{
    Desktop,
    Tablet,
    Phone
}

Step 3: Create a Driver Factory

This is going to be our longest class, so I will break the code up and explain what we’re doing here each step of the way.

Essentially, this class is where all the magic lies, and is in charge of determining which browser we want, as well as what size and for what device.

First, let’s take a look at the class that returns our ready-to-use driver:

public static IWebDriver WebDriver(Browser type, Device device)
{
    IWebDriver driver = null;

    switch (type)
    {
        case Browser.Edge:
            driver = EdgeDriver(device);
            break;
        case Browser.Firefox:
            driver = FirefoxDriver(device);
            break;
        case Browser.Chrome:
            driver = ChromeDriver(device);
            break;
        case Browser.Phone:
            driver = PhoneDriver();
            break;
    }
    return driver;
}

As you can see, we have a pretty straight forward switch block where we first determine the driver type we want: Chrome, Firefox, or Edge. You may also notice a new one at the end there, Phone, which we will get to shortly.

After we determine our browser type, we pass pass a device parameter to tell the driver to return either the desktop or tablet (or phone!) version of that driver.

Let’s dig into the ChromeDriver method:

private static ChromeDriver ChromeDriver(Device device)
{
    ChromeOptions options = new ChromeOptions();
    switch (device)
    {
        case Device.Desktop:
            options.AddArgument("--start-maximized");
            break;
        case Device.Tablet:
            options.EnableMobileEmulation("iPad");
            break;
    }
    return new ChromeDriver(driversPath, options);
}

This one is fairly straight forward. We are switching between desktop and tablet, and setting the window size accordingly.

The EnableMobileEmulation method is taking iPad as the parameter and passing the iPad user agent to our driver. This is an out of the box method for Chrome, and you can pass along a multitude of device names. You can view a ton of options by opening up Developer Tools > pressing Ctrl + Shift + M > click on the device drop down shown here:

Next, let’s look at the EdgeDriver method. Since EdgeDriver is using Chromium, you will notice its similarity to the ChromeDriver method:

private static EdgeDriver EdgeDriver(Device device)
{
    EdgeOptions options = new EdgeOptions
    {
        UseChromium = true
    };
    EdgeDriver _driver = null;
    switch (device)
    {
        case Device.Desktop:
            options.AddArgument("--start-maximized");
            _driver = new EdgeDriver(driversPath, options);
            break;
        case Device.Tablet:
            options.EnableMobileEmulation("iPad");
            _driver = new EdgeDriver(driversPath, options);
            break;
    }
    return _driver;
}

Firefox, however, is special and doesn’t have the EnableMobileEmulation method that Edge Chromium and Chrome do. We have to explicitly specify the window sizes to match our devices:

private static FirefoxDriver FirefoxDriver(Device device)
{
    FirefoxDriverService service = FirefoxDriverService.CreateDefaultService(driversPath, "geckodriver.exe");
    FirefoxOptions options = new FirefoxOptions();
    switch (device)
    {
        case Device.Desktop:
            options.AddArgument("--width=1200");
            options.AddArgument("--height=700");
            break;
        case Device.Tablet:
            options.AddArgument("--width=768");
            options.AddArgument("--height=700");
            break;
    }
    return new FirefoxDriver(service, options);
}

If you wish to explicitly specify the window size for Chrome or Edge, you can do so easily by passing the argument to your driver: options.AddArgument("--window-size=1200,700");

Finally, let’s take a look at our PhoneDriver method:

private static IWebDriver PhoneDriver()
{
    ChromeOptions options = new ChromeOptions();
    options.EnableMobileEmulation("iPhone X");
    return new ChromeDriver(driversPath, options);
}

Essentially, we are making use of the same method we used with ChromeDriver on a tablet view. Instead, we are now passing iPhone X as the parameter value to display the phone view.

Here is our DriverFactory class in its entirety:

public class DriverFactory
{

    private static string driversPath = String.Concat(Environment.CurrentDirectory, "\\drivers");

    public static IWebDriver WebDriver(Browser type, Device device = Device.Desktop)
    {
        IWebDriver driver = null;

        switch (type)
        {
            case Browser.Edge:
                driver = EdgeDriver(device);
                break;
            case Browser.Firefox:
                driver = FirefoxDriver(device);
                break;
            case Browser.Chrome:
                driver = ChromeDriver(device);
                break;
            case Browser.Phone:
                driver = PhoneDriver();
                break;
        }
        return driver;
    }

    private static IWebDriver PhoneDriver()
    {
        ChromeOptions options = new ChromeOptions();
        options.EnableMobileEmulation("iPhone X");
        return new ChromeDriver(driversPath, options);
    }

    private static ChromeDriver ChromeDriver(Device device)
    {
        ChromeOptions options = new ChromeOptions();
        switch (device)
        {
            case Device.Desktop:
                options.AddArgument("--start-maximized");
                break;
            case Device.Tablet:
                options.EnableMobileEmulation("iPad");
                options.AddArgument("--window-size=768,700");
                break;
        }
        return new ChromeDriver(driversPath, options);
    }

    private static FirefoxDriver FirefoxDriver(Device device)
    {
        FirefoxDriverService service = FirefoxDriverService.CreateDefaultService(driversPath, "geckodriver.exe");
        FirefoxOptions options = new FirefoxOptions();
        switch (device)
        {
            case Device.Desktop:
                options.AddArgument("--width=1200");
                options.AddArgument("--height=700");
                break;
            case Device.Tablet:
                options.AddArgument("--width=768");
                options.AddArgument("--height=700");
                break;
        }
        return new FirefoxDriver(service, options);
    }


    private static EdgeDriver EdgeDriver(Device device)
    {
        EdgeOptions options = new EdgeOptions
        {
            UseChromium = true
        };
        EdgeDriver _driver = null;
        switch (device)
        {
            case Device.Desktop:
                options.AddArgument("--start-maximized");
                _driver = new EdgeDriver(driversPath, options);
                break;
            case Device.Tablet:
                options.EnableMobileEmulation("iPad");
                _driver = new EdgeDriver(driversPath, options);
                break;
        }
        return _driver;
    }
}

Step 4: Create the Tests

For our testing, we are going to be using the site automationpractice.com, a testing sandbox to mimic a responsive e-commerce application.

Automation Project Responsiveness

What our tests are going to do is verify that mobile components exist on the phone view (and on tablet view, if applicable), but not on the desktop view. And vice versa!

This will be a very simple example to showcase how our tests handle the responsiveness of the test application.

First, let’s take a look at the BaseClass:

public class BaseClass
{
    public static IWebDriver _driver;
    public static string _applicationPath = "http://automationpractice.com/index.php";
    public Browser _browser;
    public Device _device;

       [SetUp]
    public void SetUp()
    {
        _driver = DriverFactory.WebDriver(_browser, _device);
        _driver.Navigate().GoToUrl(_applicationPath);
    }

    [OneTimeTearDown]
    public static void TearDown()
    {
        _driver.Quit();
    }
}

Looking at lines 5, 6 and 11, we are instantiating the test fixture’s Browser and Device objects. We are then passing the values along to our DriverFactory to create our driver instance.

But how are we getting the parameter values?

NUnit Parameterized Test Fixture

[TestFixture(Browser.Chrome, Device.Desktop)]
[TestFixture(Browser.Firefox, Device.Desktop)]
[TestFixture(Browser.Edge, Device.Desktop)]
public class DesktopTests : BaseClass
{
    public DesktopTests(Browser browser, Device device)
    {
        _browser = browser;
        _device = device;
    }

    [Test]
    public void MobileView_Displayed_False()
    {
        Assert.IsFalse(_driver.FindElement(mobileMenu).Displayed, "The mobile menu is displayed");
        Assert.IsFalse(_driver.FindElement(mobileTabletQuickViewIcon).Displayed, "The mobile/tablet quick view icon is displayed.");
    }
}

Looking at the highlighted lines above, you can see that we have annotated our test class (DesktopTests in this case) with TestFixture. In NUnit, we can parameterize our TestFixture, which can then set the value of the parameter in the test’s constructor, and pass it back to our tests.

I won’t go into the details of the magic, but you can read more about NUnit’s TestFixture class here.

Keen eyes will also note that we have three defined TestFixture attributes, each with different Browser enum value, but all with the Device.Desktop enum value. This will ensure we run the tests three times, once for each browser type on the desktop view.

So in order to do the same for Device.Tablet, we do the following:

    [TestFixture(Browser.Chrome, Device.Tablet)]
    [TestFixture(Browser.Firefox, Device.Tablet)]
    [TestFixture(Browser.Edge, Device.Tablet)]
    public class TabletTests : BaseClass
    {
        public TabletTests(Browser browser, Device device)
        {
            _browser = browser;
            _device = device;
        }

        [Test]
        public void MobileView_Displayed_False()
        {
            Assert.IsFalse(_driver.FindElement(mobileMenu).Displayed, "The mobile menu is displayed");
            Assert.IsTrue(_driver.FindElement(mobileTabletQuickViewIcon).Displayed, "The mobile/tablet quick view icon is not displayed.");
        }
    }

And finally, for phone, we pass along the following:

[TestFixture(Browser.Phone, Device.Phone)]
public class PhoneTests : BaseClass
{
    public PhoneTests(Browser browser, Device device)
    {
        _browser = browser;
        _device = device;
    }

    [Test]
    public void MobileView_Displayed_True()
    {
        Assert.IsTrue(_driver.FindElement(mobileMenu).Displayed, "The mobile menu is not displayed");
        Assert.IsTrue(_driver.FindElement(mobileTabletQuickViewIcon).Displayed, "The mobile/tablet quick view icon is not displayed.");
    }
}

And that’s it, you’re done!

You can build onto this framework to include more device types beyond Apple products, as well as passing explicit viewports in addition to the browser and device type. Whatever you want to do, it’s incredibly extensible.

+4

Related Post

1 Comment

  1. How to Automate Responsive Applications - Cross Browser Testing (Selenium, Java, JUnit 4) - Kerry McKeever

    […] a previous post I outlined how to perform cross browser testing on a responsive application leveraging C# and NUnit […]

    +2

Leave A Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.