Sunday, March 11, 2018

Deprecating Parts of Selenium's .NET Bindings

Note: This blog post should be considered a work-in-progress until this note is removed.

With the release of 3.11.0 of the Selenium .NET bindings, a few things in the support library (WebDriver.Support.dll) have been marked with the Obsolete attribute. This will come as something of a surprise for some users when they update to that version. In particular, the .NET implementation of the PageFactory and the ExpectedConditions class used with WebDriverWait have been deprecated. It's understandable that there would be some consternation about suddenly seeing compile warnings mentioning removing components in a future release of Selenium, particularly if one's own code makes use of those components. Why would the .NET bindings' maintainers do this?

First, one must consider the original intent of the WebDriver.Support.dll assembly. When originally created, it was originally designed to showcase some of the things that would be possible to create based on the WebDriver API. It was not intended that large numbers of users would plug those examples directly into production code.

Second, 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.

With respect to ExpectedConditions, again, this was an addition that was created in .NET solely because "Java has it." At the time the ExpectedConditions class in Java was created, the syntax for creating a lambda function (or something that acted like one) was particularly arcane and difficult to understand. In that case, a helper class made lots of sense for the Java bindings. However, C# isn't Java. In C#, the syntax for creating lambda functions ("anonymous methods" in the language of Microsoft's documentation) has been well understood by C# developers for many years, and is a standard tool in their arsenal.

In this case, the question of code verbosity does have some merit, but since wait conditions are rarely one-size-fits-all, it would be a much cleaner approach for users to develop their own conditions class that has the wait conditions they're interested in. This, however, is something users have an aversion to. Additionally, the thought of a "standard" collection of implementations of specific wait conditions seems to be a good idea on its face, but there is a great deal of variation on the way users want any given condition to work. Having a collection of wait conditions might be a good thing, but the Selenium project is not the place for it.

So that people would still have access to the existing implementations, a new organization has been set up on Github. The code for these two pieces of the support library have been migrated there, and the first binary artifacts have been distributed concurrent with Selenium 3.11. People can move over to the ported implementations with minimal change (usually just a namespace change) to their own code. The new repo is awaiting someone from the community who feels strongly about maintaining these types of libraries.