PageFactory in C# is deprecated as of version 3.11. This is actually a great change because it prevents usage of a class that is not recommended by the Selenium contributors. Furthermore, not using PageFactory will prevent users from many weird element exceptions that aren’t experienced when using a simple runtime element locator.

ExpectedConditions is also deprecated out of Selenium WebDriver Nuget package.

Regardless, both of these classes can be found in a new Nuget package called Selenium.Support. You can download this package and still use ExpectedConditions.

👇Don’t use PageFactory regardless as explained below.👇

What is PageFactory in C#

PageFactory in Selenium WebDriver is a class that was designed to help with the creation of Page Objects.

A test using PageFactory looks like this

    class HomePage
        private IWebDriver driver;
        [FindsBy(How = How.Id, Using = "account")]
        public IWebElement MyAccount { get; set; }

Say Goodbye to PageFactory

If you look at the release notes for Selenium .NET release of 3.11, the PageFactory.cs is moving to a different Nuget package.

Honestly, I’m grateful… the implementation caused many problems for many users. Such as the infamous StaleElementException. Let’s take a look why the Selenium Contributors don’t recommend the use of PageFactory.

Why not to use PageFactory

In summary, PageFactory is a “cumbersome and problematic implementation that is deeply flawed” and provides “no benefit to be gained over doing a find element directly in runtime code” (Jim Evans, maintainer of C# bindings).

But keep reading if you want to learn more…

Page Factory does not, by default, store the result when the driver locates the element. That means that for each thing you do with the element, PageFactory relocates it first.

Titus On Testing, Titus Fortner, Core Selenium Contributor

PageFactory does provide an annotation for caching elements: @CacheLookup. This may seem like a great idea, but before you go and add it to each of your element definitions, beware of its primary drawback, the dreaded StaleElementException. Chromedriver references an element by its location in the Document Object Model (DOM). If the DOM is reloaded by refreshing the page, or it changes in such a way that the element moves positions within the DOM, Selenium will consider that element stale.

Titus On Testing, Titus Fortner, Core Selenium Contributor

This change reduces complexity in the Selenium code base and will stop people from posting examples online using the PageFactory 👏🎉

This in no way means that you should stop using Page Objects Pattern. This is totally a separate concept. You can use Page Object Pattern without the PageFactory.cs. The latter is just a Selenium implementation that has no relation with a design pattern.

“The .NET implementation of these constructs was created mostly because some users asked, “Java has it, so why doesn’t .NET?” Rather than blindly copying the Java implementations as was done, it would have been better to think about what actually makes sense when using C#. In other words, “C# isn’t Java, and therefore the things that work best for Java may not be entirely appropriate for C#.”

In the case of the .NET PageFactory, the implementation was problematic and cumbersome, as well as not nearly flexible enough for the myriad ways people wanted to create Page Objects. Additionally, when .NET Core 2.0 was released, the classes upon which the .NET PageFactory relied were not included .NET Core 2.0. This meant that to get the PageFactory working under .NET Core, the project either had to take on a new dependency, mangle the code with conditional compile directives, or leave it unsupported in .NET Core. The first approach is a non-starter for the Selenium project’s .NET bindings, the reasons for which should be a subject of its own blog post.

The second approach made the code nearly impossible to properly maintain.

Furthermore, with respect to the PageFactory in particular, there is no benefit to be gained by identifying elements via an attribute over doing it directly in runtime code. Claims that the PageFactory made Page Object creation and maintenance less verbose simply do not hold up under close scrutiny.”

Jim Evans, maintainer of C# bindings

What to use instead of PageFactory?

Avoiding PageFactory is very simple using Acceptance Test Driven Automation. Here’s an example of a class that meets all Page Object best practices.

public class ProductsPage : BasePage
private readonly string _pageUrlPart;
public ProductsPage(IWebDriver driver) : base(driver)
_pageUrlPart = "inventory.html";
// An element can be located using ExpectedConditions through an explicit wait
public bool IsLoaded => Wait.UntilIsDisplayedById("inventory_filter_container");
//elements are not accessible for the external test API
private IWebElement LogoutLink => _driver.FindElement(By.Id("logout_sidebar_link"));
// An element can also be located without ExpectedConditions
private IWebElement HamburgerElement => _driver.FindElement(By.ClassName("bm-burger-button"));
public int ProductCount =>
//We are using Composition to have one page object living in another page object
public CartComponent Cart => new CartComponent(_driver);
public void Logout()
view raw GoodPageObject.cs hosted with ❤ by GitHub

If you are taking the Complete Selenium WebDriver with C# course, you don’t have to worry about this 👏 The correct technique using ATDA without PageFactory is the fundamental practice.


If you’ve grown to love the WebDriverWait and ExpectedConditions.cs as I have, this comes as a blow.

When I first saw this, I did a double-take… Huh? What?

So why did the .NET implementation of ExpectedConditions move to a separate code base?

According to my conversation with Jim Evans, here is why:

Yes, it reduces the size of the code base, as well as the maintenance burden. It reduces the number of times the maintainer has to respond to issues and PRs for that class. This is the proper path forward for the community to be able to enhance and maintain these methods, as the class provided by the Selenium project was frozen long ago, with PRs adding new methods being rejected.


I’ve never contributed any code to Selenium and have never had to maintain it. But I do know that software maintenance can suck…

So we do all that we can to reduce it. However, in this case, it seems to be a mistake.

I personally use the ExpectedConditions.cs on a regular basis. And I find it very useful.

Here’s the deal:

As a user of the software, my life should be easy. Instead, I now have to write my own lambda expressions to figure out if an element is in a valid state on a page.

Ugh! Definitely extra work for me. Especially when I mess up the logic 🙁

Again, I don’t support the project and don’t understand their point of view. But it seems silly to remove maintenance work from the project by decreasing usability for the end user.

But what do I know? When I create a software like Selenium, maybe then I should be allowed to complain 🙂

Instead, I’ll stop whining and tell you how you can fix these problems…

So what can you do?

First, prepare yourself for the loss of PageFactory. If you are taking the Complete Selenium WebDriver with C# course, you don’t have to worry about this 👏 The correct technique using ATDA without PageFactory is the fundamental practice.

Second, prepare yourself for the broken code that will occur on a future Selenium version (>3.11) when the Selenium project removes ExpectedConditions.cs.

To deal with this problem, you can download the Nuget Package called DotNetSeleniumExtras.WaitHelpers. Then, just reference the appropriate namespace in your files.

Also, don’t forget to do this in your using statements so that ExpectedConditions is being used from the correct Nuget package:

using ExpectedConditions = SeleniumExtras.WaitHelpers.ExpectedConditions;

Here’s a code example of me using this class: