Striter Alfa
Striter Alfa

Reputation: 1577

Selenium WebDriver in C# - "By" class Extension

I've been working in a webdriver project to automate my tests and I tried to develop something like a helper. As default, there are in the By class methods like CssSelector(), XPath(), ClassName() etc.

My question is: Is possible (and how?!) i can extend this class to create another methods to simplify my code? I want the By calls to my own methods. I searched and got some result how to create a NewBy, but i think it can make my code disorganized futurely.

What exists by default in Selenium:

FindElement(By.CssSelector("#example"));

What I'm trying to develop:

FindElement(By.MyOwnMethod("example"));

Thanks!

Upvotes: 2

Views: 3734

Answers (3)

ruffin
ruffin

Reputation: 17473

If you want your driver to have shortcuts that utilize use case specific DOM, you can always extend IWebDriver instead of By.

using OpenQA.Selenium;

namespace Tests.Selenium.Utils
{
    public static class DriverExtensions
    {
        public static IWebElement FindAdminMenu(this IWebDriver driver, string Id)
        {
            return driver.FindElement(By.CssSelector(
                string.Format("[selenium-admin-menu='{0}']", Id)
            ));
        }
    }
}

Then this line from Xiaoy312...

var menu3 = driver.FindElement(Via.AdminMenu("3"));

gets reduced to

var menu3 = driver.FindAdminMenu("3");

JeffC's critique is an interesting one. FindAdminMenu could be opaque, especially if you're really keying on ints. You'd probably do better to use human readable class names there if you can change the way the DOM is written.

With human readable classes, I wouldn't be concerned about convenience methods. What you're doing then really isn't that much different from Page Object Modeling, which for Selenium, I think, is usually A Good Thing. POM will keep your tests' actions/business logic layer abstracted from DOM changes. And there will be DOM changes [as or much more often than business logic changes]. ;^)

Upvotes: 1

Xiaoy312
Xiaoy312

Reputation: 14477

In css, the # sign is the ID selector. You can just use the By.Id(string) method.


EDIT :

If you really want to customize it, you can define your own implementation of By. However you can't extend static method, therefore you will need create a new class to host them. I'll use Via here, you can always use a more meaningful name to you, like the website name :

public class Via
{
    public static By AdminMenu(string Id) // could also just use int
    {
        return By.CssSelector(string.Format("[selenium-admin-menu='{0}']", Id));
    }

    public static By ImgSrc(string source)
    {
        return By.CssSelector(string.Format("img[src='{0}']", source));
    }
}

//usage: 
var menu3 = driver.FindElement(Via.AdminMenu("3"));

Upvotes: 3

JeffC
JeffC

Reputation: 25730

In short, you shouldn't do this. This doesn't make your code cleaner, it makes it harder to read because anyone else with Selenium experience isn't going to be able to read your code. Don't add another layer of abstraction, just use Selenium as written. If you have HTML where there are multiple admin menus that are only different by an index, e.g. By.CssSelector("[selenium-admin-menu='3']") then write a function that takes an index and returns the correct element.

public WebElement getAdminMenu(int index)
{
    return driver.findElement(By.cssSelector("[selenium-admin-menu='" + index + "']"));
}

or better yet, create a function for each admin menu based on its actual name so that consumers of the function will know exactly what that function does and don't have to look at the HTML of the page and count admin menus to return the correct one.

public WebElement getAdminMenuManageUsers()
{
    return driver.findElement(By.cssSelector("[selenium-admin-menu='1']"));
}

public WebElement getAdminMenuManageSite()
{
    return driver.findElement(By.cssSelector("[selenium-admin-menu='2']"));
}

Clearly these are made up examples since I don't know your site.

Upvotes: 2

Related Questions