Page Objects- Partial Classes Base Pages- WebDriver C#

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.

This is the eighth article from the WebDriver Page Objects Series. The copy-paste development is popular among the QA folks in the test automation world. Here, I will show you a couple of techniques to increase the code reuse in your page objects through the usage of base pages objects.

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

Initial Version without Base Page Objects

First, let us see how a regular page object (without base page) looks like and how we use it in tests. Then we are going to refactor it and make it use a base class.

SearchEngineMainPage.Actions NoBasePage Version

public partial class SearchEngineMainPage
{
    private readonly IWebDriver _driver;
    private readonly string _url = @"searchEngineUrl";
    public SearchEngineMainPage(IWebDriver browser) => _driver = browser;
    public void Navigate() => _driver.Navigate().GoToUrl(_url);
    public void Search(string textToType)
    {
        SearchBox.Clear();
        SearchBox.SendKeys(textToType);
        GoButton.Click();
    }
}

SearchEngineMainPage.Elements NoBasePage Version

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

SearchEngineMainPage.Asserts NoBasePage Version

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

SearchEngineMainPage NoBasePage Version in Tests


public class SearchEngineTests
{
    private IWebDriver _driver;
    
    public void TestInitialize()
    {
        _driver = new FirefoxDriver();
        _driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30);
    }
    
    public void TestCleanup()
    {
        _driver.Quit();
    }
    
    public void SearchTextInSearchEngine_First()
    {
        var searchEngineMainPage = new SearchEngineMainPage(_driver);
        searchEngineMainPage.Navigate();
        searchEngineMainPage.Search("Automate The Planet");
        searchEngineMainPage.AssertResultsCount("236,000 RESULTS");
    }
}

With this version, we need to hold a separate instance of WebDriver in each page object. If the page needs to have a navigate logic, we need to implement it over and over again. The previous conclusions are valid for another logic that we can repeatedly use in the pages.

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

Second Version Page Objects with Base Pages

SearchEngineMainPage.Actions Base Page Version

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

SearchEngineMainPage Base Page Version in Tests

The only change compared to the previous version is that SearchEngineMainPage derive now from the AssertedNavigatablePage abstract base class.

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();
}

AssertedNavigatablePage

public abstract class AssertedNavigatablePage : NavigatablePage
{
    protected AssertedNavigatablePage(IWebDriver driver) : base(driver)
    {
        // Resolve the IAssert through some of the popular IoC containers.
        ////Assert = ServiceContainer.Provider.Resolve<IAssert>();
    }
    protected IAssert Assert { get; }
}

We use this base class if we need to have the methods and properties from the previous two and on top of that be able to make assertions. The IAssert interface exposes methods that are not test framework agnostic. 

The usage in tests stays entirely the same.

Related Articles

Design Patterns

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.

Page Objects- App Design Pattern- WebDriver C#

Design Patterns

Advanced Null Object Design Pattern in Automated Testing

This is the second article dedicated to the Null Object Design Pattern part of the Design Patterns in Automated Testing series. In the last post, I showed you h

Advanced Null Object Design Pattern in Automated Testing

Design Patterns

Composite 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

Composite Design Pattern in Automated Testing

Design Patterns, Web Automation Java

Mastering Parameterized Tests in JUnit with Selenium WebDriver

In the evolving landscape of software testing, efficiency and coverage are paramount. JUnit 5 introduces enhanced parameterized testing capabilities, allowing d

Mastering Parameterized Tests in JUnit with Selenium WebDriver

Design Patterns

Page Objects- Partial Classes Singleton 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.

Page Objects- Partial Classes Singleton Design Pattern- WebDriver C#

Design Patterns

Singleton Design Pattern in Automated Testing

Ensure a class has only one instance and provide a global point of access to it.Instance control– prevents other objects from instantiating their own copies of

Singleton 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.