Richard North’s Blog

Upcoming fea­ture: au­to­matic Selenium API ver­sion match­ing in Testcontainers

A key de­sign goal for the Selenium sup­port in Testcontainers is to pre­vent the spo­radic break­age of the com­pat­i­bil­ity be­tween Selenium li­brary JARs and the browsers they in­ter­face with. In the past, I’ve seen count­less ex­am­ples of night­mares when pro­jects’ Selenium li­brary de­pen­den­cies don’t match the browsers that de­vel­op­ers have in­stalled lo­cally. This trend be­came worse when browsers started re­ceiv­ing au­to­matic up­dates.

The first re­leases of Testcontainers took an al­ter­na­tive ap­proach: use tagged docker con­tain­ers for web browsers to lock browser ver­sions to be com­pat­i­ble with a spe­cific ver­sion of the Selenium API - namely v2.45.0. This lever­ages the ex­cel­lent SeleniumHQ docker-se­le­nium pro­jec­t’s set of docker con­tain­ers. This means that any pro­ject us­ing Testcontainers sim­ply needed to match its selenium-*.jar de­pen­dency ver­sions to ver­sion 2.45.0, and com­pat­i­bil­ity would be as­sured.

However, I was con­scious that this ap­proach would be­come painful as newer, bet­ter ver­sions of Selenium are re­leased. Keeping all users of Testcontainers per­pet­u­ally locked to us­ing an old ver­sion of Selenium does­n’t seem like a de­vel­oper-friendly thing to do.

Options for change #

Option 1 would have been the sim­plest thing for me to do: al­low users to spec­ify which ver­sion of the docker-se­le­nium con­tain­ers to use. For ex­am­ple,

this in the POM:

<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
<version>2.52.0</version>
</dependency>

would need to be paired with this in the JUnit tests:

@Rule
public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer()
.withDesiredCapabilities(DesiredCapabilities.chrome())
.withSeleniumVersion("2.52.0"); // don't try this!

However, de­clar­ing a ver­sion in two places in this man­ner is not DRY and quite ugly - not only are we forc­ing ex­tra boil­er­plate on the de­vel­oper, but it needs to be main­tained and kept in sync.

Is there an al­ter­na­tive way, where the ver­sion would only need to be spec­i­fied in a sin­gle place?

It turns out there is a pretty neat Option 2: de­ter­mine the pro­jec­t’s ver­sion of Selenium at run time. I found that the selenium-api li­brary is pack­aged with an in­cred­i­bly use­ful piece of meta­data in its META-INF/MANIFEST.MF file:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: linman
Created-By: Apache Maven 3.1.1
Build-Jdk: 1.8.0_72

Name: Build-Info
Selenium-Build-Time: 2016-02-11 11:22:43
Selenium-Version: 2.52.0		<----------- AHA!
Selenium-Revision: 4c2593cfc3689a7fcd7be52549167e5ccc93ad28

The cru­cial Selenium-Version at­tribute al­lows us to se­lect tagged ver­sions of Chrome and Firefox that were tested for com­pat­i­bil­ity with this spe­cific ver­sion of Selenium.

Manifests are eas­ily over­looked, but oc­ca­sion­ally al­low a small gem of meta func­tion­al­ity such as this. It’s great that the Selenium de­vel­op­ers were thought­ful enough to in­clude this kind of meta­data.

Outcome #

The ap­proach that I’ve taken (which should be in v1.0.3 of Testcontainers) is:

Conclusion #

I’m pretty happy with this ap­proach; it re­solves a lin­ger­ing is­sue that I’d been mean­ing to sort out. This so­lu­tion seems bet­ter than the other

Additionally, it aligns with my be­lief that li­brary au­thors should go the ex­tra mile to achieve a sim­ple API for their users (developers) - even if it means a bit of ex­tra work is needed on the in­ter­nals. Many of the de­sign de­ci­sions I’ve taken with Testcontainers so far are based on this be­lief, and I’m happy to be able to in­clude this kind of fea­ture.

What do you think? Please com­ment on the PR or drop me an email!

← Home