Page Objects- App Design Pattern- WebDriver C#

Page Objects- App Design Pattern- WebDriver C#

Editorial Note: I originally wrote this post for the Test Huddle Blog. You can check out the original here, at their site.

This is the tenth article from the WebDriver Page Objects Series. Here I am going to share with you how to create a single place for handling the WebDriver initializations and creation of page objects. I call it App (short for application).

Two years ago while we were working on the first version of the BELLATRIX test automation framework, I did this research so that we can find the most convenient way for creating page objects.

Test Case

We will once again automate the main SearchEngine page. All of the code is placed inside the SearchEngineMainPage class.

SearchEngine Main Page

Page Objects with Base Pages

All of our page objects need to derive from some of the base page classes which you can find after the page object’s code.

SearchEngineMainPage.Actions

public partial class SearchEngineMainPage : AssertedNavigatablePage
{
    public SearchEngineMainPage(IWebDriver driver) : base(driver)
    {
    }
    public override string Url => @"actualUrlToSite";
    public void Search(string textToType)
    {
        SearchBox.Clear();
        SearchBox.SendKeys(textToType);
        GoButton.Click();
    }
}

Because of the base class, we can use in tests the GoTo and the Refresh methods. Also, we have access to the Assert property.

SearchEngineMainPage.Elements

public partial class SearchEngineMainPage
{
    public IWebElement SearchBox => WrappedDriver.FindElement(By.Id("sb_form_q"));
    public IWebElement GoButton => WrappedDriver.FindElement(By.Id("sb_form_go"));
    public IWebElement ResultsCountDiv => WrappedDriver.FindElement(By.Id("b_tween"));
}

We use the WrappedDriver protected property that comes from the base classes to locate the elements.

SearchEngineMainPage.Asserts

public partial class SearchEngineMainPage
{
    public void AssertResultsCount(string expectedCount) => Assert.AreEqual(ResultsCountDiv.Text, expectedCount);
}

We use the Assert protected property that comes from the base class to perform the validations. This property is test framework agnostic, which means that we can use it with MSTest or NUnit. The IAssert interface exposes methods that are not test framework agnostic. You can read more about the concrete implementation in my article- Create Hybrid Test Framework- Abstract Unit Test Framework

Page

This is the most basic base page object that we might have. It holds only a protected WebDriver property. We use this base page for pages that we do not need to navigate to them. E.g., middle pages in some wizard or something similar.

public abstract class Page
{
    protected Page(IWebDriver driver) => WrappedDriver = driver;
    protected IWebDriver WrappedDriver { get; }
}

If we need to expose a navigate logic, you can use the below base class. It adds to the child page a GoTo and Refresh methods and obligates the page to set its URL.

public abstract class NavigatablePage : Page
{
    protected NavigatablePage(IWebDriver driver) : base(driver)
    {
    }
    public abstract string Url { get; }
    public void GoTo() => WrappedDriver.Navigate().GoToUrl(Url);
    public void Refresh() => WrappedDriver.Navigate().Refresh();
}

App Design Pattern in Tests


public class SearchEngineTests
{
    private App _app;
    
    public void TestInitialize()
    {
        _app = new App();
    }
    
    public void TestCleanup()
    {
        _app.Dispose();
    }
    
    public void UseApp_SearchTextInSearchEngine_UseElementsDirectly()
    {
        var searchEngineMainPage = _app.GoTo<SearchEngineMainPage>();
        searchEngineMainPage.Search("Automate The Planet");
        searchEngineMainPage.AssertResultsCount("236,000 RESULTS");
    }
}

As you can see, we do not have any WebDriver initialization code in the tests. Everything is happening behind the scenes the App class. Moreover, the App instance is responsible for the initialization and navigation to the different page objects. Here, we use the generic GoTo method to navigate to the SearchEngineMainPage.

App

public class App : IDisposable
{
    private IWebDriver _driver;
    public App(BrowserType browserType = BrowserType.Firefox)
    {
        StartBrowser(browserType);
    }
    public void Dispose() => _driver.Dispose();
    public TPage Create<TPage>()
    where TPage : Page
    {
        var constructor = typeof(TPage).GetTypeInfo().GetConstructors(BindingFlags.NonPublic | Bind - ingFlags.Instance).FirstOrDefault();
        var page = constructor.Invoke(new object[] { _driver }) as TPage;
        return page;
    }
    public TPage GoTo<TPage>()
    where TPage : NavigatablePage
    {
        var constructor = typeof(TPage).GetTypeInfo().GetConstructors(BindingFlags.NonPublic | Bind - ingFlags.Instance).FirstOrDefault();
        var page = constructor.Invoke(new object[] { _driver }) as TPage;
        page.GoTo();
        return page;
    }
    private void StartBrowser(BrowserType browserType = BrowserType.Firefox)
    {
        switch (browserType)
        {
            case BrowserType.Firefox:
                _driver = new FirefoxDriver();
                break;
            case BrowserType.InternetExplorer:
                break;
            case BrowserType.Chrome:
                break;
            default:
                throw new ArgumentException("You need to set a valid browser type.");
        }
    }
}

In the constructor of the class, we start the browser. We have two generic methods for creation of the page objects. We use reflection to initialize the objects. However, there are various ways to do the same thing even better. I prefer to use inversion of control containers or service locators. You can read more about the IoC containers in my article- Use IoC Container to Create Page Object Pattern on Steroids

For more detailed overview and usage of many more design patterns and best practices in automated testing, check my book “Design Patterns for High-Quality Automated Tests, C# Edition, High-Quality Tests Attributes, and Best Practices”.  You can read part of three of the chapters:

Defining High-Quality Test Attributes for Automated Tests

Benchmarking for Assessing Automated Test Components Performance

Generic Repository Design Pattern- Test Data Preparation

Related Articles

Design Patterns

Proxy Design Pattern in Automated Testing

Achieving high-quality test automation that brings value- you need to understand core programming concepts such as SOLID and the usage of design patterns. In th

Proxy Design Pattern in Automated Testing

Design Patterns

Decorator Design Pattern in Automated Testing

In my articles "Strategy Design Pattern" and "Advanced Strategy Design Pattern", I explained the benefits of the application of Strategy Design Pattern in your

Decorator Design Pattern in Automated Testing

Design Architecture, Design Patterns

Highlight Elements on Action- Test Automation Framework Extensibility through Observer Design Pattern

As you know, in past articles from the Design and Architecture Series I wrote about the 5th generation test automation frameworks or as I like to call them Full

Highlight Elements on Action- Test Automation Framework Extensibility through Observer Design Pattern

Design Patterns

Page Objects- Partial Classes Base Pages- WebDriver C#

Editorial Note: I originally wrote this post for the Test Huddle Blog. You can check out the original here, at their site.

Page Objects- Partial Classes Base Pages- WebDriver C#

Design Patterns

Specification Design Pattern in Automated Testing

If you follow my series about Design Patterns in Automated Testing, I explain how you can utilize the power of various design patterns in your tests. In the cur

Specification Design Pattern in Automated Testing

Design Patterns

Advanced Specification Design Pattern in Automated Testing

In my last publication from the Design Patterns in Automated Testing Series, I explained to you how you can benefit from the usage of the Specification Design P

Advanced Specification Design Pattern in Automated Testing
Anton Angelov

About the author

Anton Angelov is Managing Director, Co-Founder, and Chief Test Automation Architect at Automate The Planet — a boutique consulting firm specializing in AI-augmented test automation strategy, implementation, and enablement. He is the creator of BELLATRIX, a cross-platform framework for web, mobile, desktop, and API testing, and the author of 8 bestselling books on test automation. A speaker at 60+ international conferences and researcher in AI-driven testing and LLM-based automation, he has been recognized as QA of the Decade and Webit Changemaker 2025.