Apr 30

Managing Multiple Private SSH Keys

Until recently, I wasn’t really aware of the concept of having multiple private/public SSH key pairs in order to access different systems. As a developer, I’ve obviously used SSH fairly frequently over a number of years but it’s been more to get things done, e.g. setting up my GitHub account or connecting to some server at work once in a blue moon.

Typically, I would have a single pair of keys which I would use in all cases. So, my public key would be held by GitHub, my Mac server, my Gerrit server, Jenkins, TeamCity and so on. If I lost my private key it wouldn’t be a terrible loss – most of the systems I use are only accessible on the company intranet and are easily changed. I now know this is not the most secure setup (hey, I’m no sysadmin) but I also know that I’m not the only person doing this!

So what happens when we want to SSH onto a machine using a different key pair?

Manually managing multiple private keys

Let’s assume you’ve already set up new key pairs in your ~/.ssh directory. If you ls in that directory, you might see something like this:

$ ls ~/.ssh/

jenkins		jenkins.pub		github		github.pub	known_hosts

When SSH’ing onto a server – e.g. Jenkins – you would usually just type $ ssh user@host and be done with it (assuming you have the default id_rsa and id_rsa.pub keys in your ~/.ssh/ folder).

Because we now have separate keys for each server, we must specify the location of the corresponding private key (in your ~/.ssh/ directory) when we attempt to SSH onto the server with the -i flag:

$ ssh -i ~/.ssh/jenkins ben.snape@jenkins.itv.com

This does the trick but it’s very… wordy. Surely we can shorten it?

Introducing SSH config

Fortunately, there is a simple way to do this.

We can define a config in our SSH directory. The format looks like this:

Host           short-name
HostName       some.very.long.server.name
IdentityFile   ~/.ssh/private_ssh_key
User           username-on-remote-server

You can specify as many hosts as you like. Also, you may not need the User field depending on your use-case.

Let’s fill this out with my example Jenkins login from above and test it out:

Host           jenkins
HostName       jenkins.itv.com
IdentityFile   ~/.ssh/jenkins
User           ben.snape

Now we can really easily connect by typing:

$ ssh jenkins

Much better!

Apr 28

If-Less Programming – Or, How You Should be Using Polymorphism

Introduction

While reading Hacker News I came across a very interesting blog post which contained a small discussion based on a Tech Talk by Google about how and why you should avoid if statements and switches through polymorphism.

The basic premise is that if you are repeatedly checking if an object is a type of something or if some flag is set then you’re probably violating one of the fundamental concepts of Object-Oriented Programming. Rather than introducing (complicated and/or cryptic) logic for a generic and incohesive class, it is advisable to implement a concrete subclass of an abstract superclass.

Ultimately, you should be trying to implement each fundamental OOP concept (SOLID)pragmatically of course, as over-engineering a solution is just as poor practise.

Why Should We Do This?

The main benefits (taken from the Tech Talk slides) are threefold:

  1. Functions without ifs are easier to read
  2. Functions without ifs are easier to test
  3. Polymorphic systems are easier to maintain (and extend) – again, refer to the Open/Closed Principle

An Example

I’m going to adapt a non-production code (i.e. test code) refactor I recently worked on.

My main responsibility is to look after ITV’s (exposed) back-end systems, including Mercury, our video content playlist service. Given a production ID and the client platform (e.g. DotCom for ITVPlayer.com, Android for the ITV Android app etc.) then Mercury returns the requested piece of content. The platform part is important as Mercury handles many platform-specific things such as lower content bitrates for the mobile platforms and higher bitrates for YouView and the ITVPlayer site, for example.

So of course, it is necessary to test that platform-specific things work on the intended platforms only.

The wrong way fast way to complexity and technical debt

Here’s a basic scenario that I’ve already given some background on above:

  Scenario Outline: Platform bitrates
    Given I request the Mercury playlist for <platform>
    Then I get the correct bitrates for <platform>

  Examples:
    | platform |
    | DotCom   |
    | Android  |
    | Mobile   |
    | Samsung  |
    | PS3      |
    | YouView  |

Pretty straightforward. Now we can implement these steps quite naively:

Given /^I request the Mercury playlist for (\w+)$/ do |platform|
  @savon_client = @mercury.create_client
  production = @production_helpers.get_production_from_config platform
  @response = @mercury.playlist_request(@savon_client, production, platform)
end

Then /^I get the correct bitrates for (.*)$/ do |platform|
  found_bitrates = @response.xpath('//VideoEntries/Video/MediaFiles/MediaFile').map { |node| node.attr('bitrate').to_i }

  expected_bitrates = case platform
                        when /android/i then [150000, 300000, 400000, 600000, 800000, 1200000]
                        when /samsung/i then [1200000]
                        when /youview/i then [1200000]
                        when /ps3/i then [800000]
                        when /mobile/i then [400000]
                        else [400000, 600000, 800000, 1200000]
                      end

  found_bitrates.should =~ expected_bitrates
end

I think it’s implementations like this that give tools like Cucumber and its proponents a bad name. The step implementations are littered with conditional logic, unnecessary passing through of variables to various classes and a significant number of instance variables (compared to local variables).

Refactoring and removing the conditional logic

A much better approach is to properly model your domain (yes, even with testing).

Platforms are objects and should be treated as such. Mercury is also an object but it should be part of platform objects in a has-a relationship.

Let’s refactor our code from above starting with the Cucumber feature:

  Scenario Outline: Platform bitrates
    Given I have a piece of <platform> catchup content
    When I request the Mercury playlist
    Then I get the correct bitrates

  Examples:
    | platform |
    | DotCom   |
    | Android  |
    | Mobile   |
    | Samsung  |
    | PS3      |
    | YouView  |

The plan is to have a data (platform object) instantiation pre-condition in the Given step, before having generic When and Then steps which will harness our new object-oriented design.

The new steps can be simply re-written (note the meta-programming in the Given step):

Given /^I have a piece of (\w+) catchup content$/ do |platform|
  @platform = Object::const_get(platform.downcase.camelcase).new
end

When /^I request the Mercury playlist$/ do
  @platform.request_playlist
end

Then /^I get the correct bitrates$/ do
  @platform.playlist_response.bitrates.should == @platform.bitrates
end

Now we need our platform base class. The idea here is to define generic platform behaviour which the individual subclasses can override if required.

class Platform

  attr_reader :playlist_request, :playlist_response

  attr_accessor :production

  def initialize
    ...
    @production = "#{EnvConfig['generic_production']}"
    @playlist_request = Mercury::Request.new
    @playlist_response = Mercury::Response.new
    @playlist_request.data[:request][:ProductionId] = @production
  end

  def request_playlist
    @playlist_response.response = @playlist_request.do
  end

end

An example platform might look something like this:

class Youview < Platform

  attr_reader :bitrates

  def initialize
    super()
    @bitrates = [1200000]
    ...
  end

  def request_playlist
    @playlist_request.data[:siteInfo][:Platform] = 'YouView'
    super
  end

end

As a result of this new design, it is so easy to see the generic and specific behaviour for each platform. Not only that, but the test code itself is much easier to write, maintain and read. I’ve no doubt that any developer could come in and quickly get up and running.

I’ve deliberately left out the Mercury classes as they could contain some commercially sensitive information (especially the stuff around adverts). With that aside, the Mercury response class was a really important refactor as it encapsulates all the tricky xpaths and regular expression parsing of the SOAP response in one place. Again, for any platform-specific behaviour it was just a case of creating a concrete subclass of Mercury::Response to implement the differences.

Staying Out of Trouble

There is always a fine line between meaningful concrete subclassing that aids understanding versus runaway subclassing and getting caught in an inheritance nightmare.

Base (abstract) classes are a Ruby anti-pattern, yet are embraced by the Java community.

Indeed, in statically typed languages there can be lots of boiler plate code which is ripe for inheritance. However, unless you’re a fairly experienced developer who is deft with an IDE then it’s so easy to become entangled amongst so many interfaces and implementations that you don’t know which way is up (I know because I’ve been in that situation before).

Final Thoughts

The concept of complete if-less programming has certainly left an impression on me. Not that I didn’t know that having good OO principles was desirable when designing software, I simply wasn’t aware that there was a movement around this concept.

I think that it’s easy – especially when writing non-production code – to subscribe to a ‘hack away’ mentality rather than properly think things through. Deadlines are often tight, testers are often lacking experience in writing clean, maintainable code and developers tasked with writing tests don’t always take it up with enthusiasm.

But the fact remains that there is probably no better way of describing your system than through a solid set of BDD OO tests.

Apr 08

Converting a Mercurial Repository to Git

Today I had the quick task of converting an old, internally-hosted Mercurial repository to Git – and subsequently pushing the converted repository to GitHub.

It was a lot easier than I thought it would be! Hopefully this should help others as it appears there are a number of ways to do this.

The Conversion

First if all, clone the repository you wish to convert.

$ hg clone http://mercurial/old-repository

Now create a directory with the name of your project in a separate location.

$ mkdir converted-project
$ cd converted-project

Next, we need to clone the Mercurial-Git converter into the directory we just created (‘converted-project’).

$ git clone git@github.com:frej/fast-export.git .

Once that’s done, we should remove the existing Git references in our directory before re-initialising it.

$ rm -rf .git .gitignore
.git/       .gitignore 

$ git init
Initialized empty Git repository in /Users/bensnape/converted-project/.git/

We’re ready to do the actual conversion now. We simply point the bash script hg-fast-import.sh to our Mercurial repository that requires converting with the -r flag. You should see some lengthy output from the conversion.

$ ./hg-fast-export.sh -r ../converted-project/

master: Exporting full revision 1/38 with 8176/0/0 added/changed/removed files
Exported 1000/8176 files
Exported 2000/8176 files
Exported 3000/8176 files
Exported 4000/8176 files
Exported 5000/8176 files
Exported 6000/8176 files
Exported 7000/8176 files
Exported 8000/8176 files
Exported 8176/8176 files
master: Exporting simple delta revision 2/38 with 0/2/0 added/changed/removed files
master: Exporting simple delta revision 3/38 with 0/84/0 added/changed/removed files
master: Exporting simple delta revision 4/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 5/38 with 0/0/67 added/changed/removed files
master: Exporting simple delta revision 6/38 with 0/2/4 added/changed/removed files
master: Exporting simple delta revision 7/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 8/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 9/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 10/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 11/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 12/38 with 0/1/0 added/changed/removed files
master: Exporting thorough delta revision 13/38 with 0/2/0 added/changed/removed files
master: Exporting simple delta revision 14/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 15/38 with 0/2/0 added/changed/removed files
master: Exporting thorough delta revision 16/38 with 0/5/0 added/changed/removed files
master: Exporting simple delta revision 17/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 18/38 with 0/35/5 added/changed/removed files
master: Exporting thorough delta revision 19/38 with 0/36/5 added/changed/removed files
master: Exporting simple delta revision 20/38 with 2/0/0 added/changed/removed files
master: Exporting simple delta revision 21/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 22/38 with 0/1/0 added/changed/removed files
master: Exporting thorough delta revision 23/38 with 0/2/0 added/changed/removed files
master: Exporting simple delta revision 24/38 with 0/1/0 added/changed/removed files
master: Exporting thorough delta revision 25/38 with 0/2/0 added/changed/removed files
master: Exporting simple delta revision 26/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 27/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 28/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 29/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 30/38 with 1/1/0 added/changed/removed files
master: Exporting simple delta revision 31/38 with 0/5/0 added/changed/removed files
master: Exporting thorough delta revision 32/38 with 3/9/0 added/changed/removed files
master: Exporting simple delta revision 33/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 34/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 35/38 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 36/38 with 0/2/0 added/changed/removed files
master: Exporting simple delta revision 37/38 with 0/2/0 added/changed/removed files
master: Exporting simple delta revision 38/38 with 0/1/0 added/changed/removed files
Issued 38 commands
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:      10000
Total objects:         8561 (       838 duplicates                  )
      blobs  :         7596 (       797 duplicates       4821 deltas of       7333 attempts)
      trees  :          927 (        41 duplicates        219 deltas of        924 attempts)
      commits:           38 (         0 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:           1 (         1 loads     )
      marks:           1024 (        38 unique    )
      atoms:           7162
Memory total:          3079 KiB
       pools:          2610 KiB
     objects:           468 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 8589934592
pack_report: pack_used_ctr            =        648
pack_report: pack_mmap_calls          =        167
pack_report: pack_open_windows        =          1 /          1
pack_report: pack_mapped              =   40116445 /   40116445
---------------------------------------------------------------------

That’s the conversion done! Now we just need to clean up a bit.

$ git clean -f

Removing Makefile
Removing README
Removing hg-fast-export.py
Removing hg-fast-export.sh
Removing hg-reset.py
Removing hg-reset.sh
Removing hg2git.py
Removing hg2git.pyc
Removing svn-archive.c
Removing svn-fast-export.c
Removing svn-fast-export.py

We’ve now finished the conversion and cleanup.

N.B.

Don’t be concerned if you cannot see any files in your newly-converted repository when you try to view them.

$ ls -a
.	..	.git

This is completely normal. You can verify your repository exists by inspecting your Git objects:

$ git count-objects -v

count: 0
size: 0
in-pack: 8561
packs: 1
size-pack: 39411
prune-packable: 0
garbage: 0

You can even do a local filesystem clone of your repository to see what it should look like.

$ cd ..
$ git clone file://converted-project test-clone

Cloning into 'test-clone'...
remote: Counting objects: 8561, done.
remote: Compressing objects: 100% (3345/3345), done.
remote: Total 8561 (delta 5040), reused 8561 (delta 5040)
Receiving objects: 100% (8561/8561), 38.26 MiB | 34.60 MiB/s, done.
Resolving deltas: 100% (5040/5040), done.

$ cd test-clone
$ ls
Aspnet_Permissions.sql   Itv.Cms.DAL             Itv.Cms.Workflows   Itv.Web.Services.Behaviour
Basic.build              Itv.Cms.Dal.UnitTests   Itv.DMZServices     Itv.Web.UnitTests
...

Pushing to GitHub

First, create an empty repository in GitHub.

Now, go to your converted repository and add a new remote that points to the empty GitHub repo.

$ git remote add origin git@github.com:organisation/repository.git

We just need to push the converted repository to GitHub now. From the conversion there should only be a master branch (you can check this by running git branch -a).

$ git push origin master

Counting objects: 8561, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3345/3345), done.
Writing objects: 100% (8561/8561), 38.26 MiB | 677 KiB/s, done.
Total 8561 (delta 5040), reused 8561 (delta 5040)
To git@github.com:organisation/project.git
 * [new branch]      master -> master

Finished!

Dec 29

Cucumber-JVM – a BDD Zeitgeist about-turn?

As a long-term(ish) user of Java but a recent adopter (I hesitate to say convert) of Ruby and Cucumber, I’ve been aware of Cucumber-JVM — a rewrite of Cucumber in Java for the uninitiated — for some time now.

But first, some background. I reacquainted myself — and got more serious with — Java a couple of years ago (after allowing myself to get far too rusty with it after my degree finished). Getting certified re-taught me a lot of the fundamentals as well as properly uncovering the deep, dark depths of the language for the first time. At the time I was using Java with the most popular BDD framework at the time – JBehave.

With hindsight, JBehave was unwieldy, unfriendly and simply tried to do too much. I didn’t realise this at the time as verbosity and unwieldiness are often part-and-parcel of Java (and other similar languages e.g. C#). This isn’t a criticism of the language itself — it’s just how it is.

Eventually, I moved on to another company where I was exclusively using Ruby and Cucumber for the first time. Initially it was a struggle but now I fully appreciate just how powerful and flexible the language is and how strongly I identify with Ruby’s mantra and raison d’être.

Which brings us to the whole point of this post.

Ruby and Cucumber

What I love about Ruby and Cucumber is that the simplicity of each perfectly compliments the other.

There is no mandatory boilerplate.

There aren’t numerous ways to configure the execution.

It just works.

My personal transition from Java to Ruby has given me food for thought. In the same way that I learnt from my own journey, perhaps Java also can learn from the leanness of Cucumber?

Java vs. Ruby

I decided to put the idea into practise.

Today I looked at implementing a basic example from the ITV back-end test suite in Java and Cucumber-JVM. Take a look on my GitHub account for the complete example.

It’s difficult to directly compare the two implementations as they do differ very slightly in their aim and use of helper methods but you cannot deny how similar the two snippets look:

    @Given("^I request the (\\w+) Most Watched Feed for (\\w+)$")
    public void I_request_the_type_Most_Watched_Feed_for_platform(String type, String platform) throws Throwable {
        mercuryRequest = new MercuryRequest();
        response = mercuryRequest.requestMostWatchedFeed(type, platform);
    }

    @Then("^I get a successful (\\w+) response with the correct (\\w+)$")
    public void I_get_a_successful_type_response_with_the_correct_platform(String type, String platform) throws Throwable {
        String text;

        switch (RequestType.valueOf(type.toUpperCase())) {
            case JSON:
                JSONObject json = mercuryRequest.stringToJson(response);
                text = JsonPath.read(json, "$.Parameters.Platform");
                assertEquals(platform, text);
                break;
            case XML:
                Document xml = mercuryRequest.stringToXml(response);
                XPathFactory xpf = XPathFactory.newInstance();
                XPath xpath = xpf.newXPath();
                text = xpath.evaluate("//Params/Param[2]/Value", xml.getDocumentElement());
                assertEquals(platform, text);
                break;
        }
    }
Given /^I request the (\w+) (\w+) (.*) api$/ do |type, platform, uri|
  @uri = "#{EnvConfig['mercury_url']}/api/#{type}/#{platform}/#{uri}"
  @response = @mercury_api.get_response_from_url @uri
end

Then /^I get a successful (\w+) response with the correct (\w+)$/ do |type, platform|
  case type
    when 'xml'
      xml = @mercury_api.get_xml_from_response @response
      unless @mercury_api.value_exists_in_xml_node?(xml, "Value", platform)
        raise "could not find the correct platform value: #{platform} in the response for uri: #@uri"
      end
    when 'json'
      json = @mercury_api.parse_json_response @response
      @mercury_api.find_value_in_hash("Platform", json).should == platform
  end
end

Looking Ahead – The Killer Questions

I’m not trying to draw any concrete conclusions but I think the next year or two could be an interesting time for BDD development with regard to Java and Ruby (and possibly other languages).

  • Will Java learn from Cucumber (and Ruby’s) example in terms of verbosity, clarity and maintainability?
  • Will the emergence of Cucumber-JVM together with the open-source movement encourage developers/testers to focus more strongly on a common language — particularly in light of Ruby’s comparatively poor performance for enterprise applications versus Java?
  • Will Ruby truly be able to throw off the performance shackles it wears with the release of Ruby 2.0 and become more than a niche startup, prototyping and test automation language?
Dec 05

Jenkins Windows Slave JScript Compilation Error Fix

Really quick one here as this has been driving me crazy for weeks.

I’m sure it’ll help someone.

Introduction

In my Jenkins distributed build system I have a Windows slave that’s started via the recommended Java Web Start (JNLP).

Every time I restarted Jenkins (the master) or the Windows slave (i.e. broke the master/slave connection) I would get a couple of Microsoft JSscript compilation errors:

Worst of all, the Jenkins slave does not connect until the errors are dismissed. Obviously this is extremely undesirable in a headless environment.

A bug was raised here: http://issues.hudson-ci.org/browse/HUDSON-7819 but no resolution was given. A number of weeks had passed without the errors just ‘going away’.

The solution

After annoying me for weeks I finally managed to suppress the errors.

To make the errors go away, go to Control Panel > Internet Options > Advanced tab > then click the two ‘disable script debugging’ checkboxes and untick ‘display a notification about every script error’:

Sorted at last!

Nov 20

Manual Ruby Gem Version Management in Non-Ruby Projects

Another quick post from something that I worked on today.

I needed to create a Bundler-esque solution for a non-Ruby project. Often, such pragmatic decisions do not offer the cleanest approach but I required a fairly robust long-term solution.

Introduction

To give some context, I am currently migrating many year’s worth of builds from TeamCity to Jenkins. I’m not going to go into details of why I recommend Jenkins but it’s been one of my favourite tools for a long time (and Hudson before that).

A set of jobs that I’m migrating are non-Ruby but utilise a bespoke Ruby gem via a Rakefile. The problem is that a large number of jobs have a dependency on this gem but as they are not Ruby projects there are no gemspecs, hence no Bundler support.

Possible Approaches

I think there are two main approaches here:

  1. Introduce Bundler support (i.e. gemspecs) for each project.
  2. Introduce a more defensive approach as a pre-build step in Jenkins.

The first approach is difficult due to the large number of projects across various source control systems (Git, Mercurial). Furthermore, the front-end developers who the projects belong to would prefer not to be burdened with extra local build steps (i.e. complexity with a tool outside of their core skills).

Therefore the second approach was necessary. I had a few requirements:

  1. Builds had to be fast and reliable in my distributed Jenkins build system.
  2. The builds had to work on any *nix master or slave, installing any required dependencies itself.

Implementing a solution

So I began with a simple Ruby class that lives at the root directory of the bespoke gem. First, I needed to know the current version directly from the gem source.

def get_source_gem_version
  Bloomfedtasks.const_get(:VERSION).to_s
end

Where Bloomfedtasks is the name of my Ruby gem (and therefore the Ruby gem module).

I then needed an easy way to figure out what version of the gem was currently installed on the system (if at all). I wasn’t previously aware of the wealth of possible RubyGems commands there were but I easily found what I was looking for. I tested the command in a terminal window first:

$ gem query --name-matches bloomfedtasks


*** LOCAL GEMS ***

bloomfedtasks (0.3.3)

Great – all I needed now was a regular expression to capture the version and return it if found, otherwise return 0.0.0 (in other words, not installed).

def get_installed_gem_version
  version = (`gem query --name-matches bloomfedtasks`.match /(\d+\.\d+\.\d+|\d+\.\d+)/).to_s
  version == '' ? '0.0.0' : version
end

A straightforward method that decides whether to update is up next:

def update?
  get_source_gem_version > get_installed_gem_version ? true : false
end

And that’s it. The complete class looks like this:

class GemVersion

  require_relative 'lib/bloomfedtasks/version'

  class << self

    def get_source_gem_version
      Bloomfedtasks.const_get(:VERSION).to_s
    end

    def get_installed_gem_version
      version = (`gem query --name-matches bloomfedtasks`.match /(\d+\.\d+\.\d+|\d+\.\d+)/).to_s
      version == '' ? '0.0.0' : version
    end

    def update?
      get_source_gem_version > get_installed_gem_version ? true : false
    end

  end

  # true if it needs updating
  puts update?

end

Going further with a shell script

The code above is useful in so far that it tells you if your installed version of the gem is the latest. It still requires you to acknowledge that your gem is out of date before needing you to manually build and install it. A simple bash script can do this for you:

#!/bin/bash

if [ `ruby gem_version.rb` = true ]; then
  echo "Your bloomfedtasks gem needs updating. Installing now..."
  gem build bloomfedtasks.gemspec
  gem install bloomfedtasks
else
  echo "Your bloomfedtasks gem is up to date."
fi

It simply executes the Ruby script we wrote earlier and builds and installs the gem if it is out of date.

Conclusion

It was interesting to see how easy this was to put together. It has also sped up a large number of Jenkins builds by ~80% which is nice to see. Of course the biggest pitfall is forgetting to update the gem after making a change…but I’m willing to take that risk as it is not widely used or business critical.

Nov 02

MSBuild on Jenkins

Just a quick note on getting MSBuild to work for Jenkins. I’m used to developing and working within Unix/Linux environments on open-source tools so this exposed me to some new things.

I have a Jenkins distributed build system at ITV as we deal with a variety of platforms (legacy .NET systems but newer Java, Ruby and Drupal systems in the pipeline). I have a Jenkins master set up on an Mac server with a Jenkins slave on a Windows machine that builds ITV’s .NET solutions. This includes Bloom (our internal CMS), Mercury (a service that provides playlists and content for the ITV Player across all devices) and Lithium (a lightweight pipeline for news and sport content).

On my Windows Jenkins slave I already had the correct Microsoft .NET framework (v4.0.30319) installed.

As an MSBuild newbie (I’m more a Maven and Bundler kind of guy), I naively typed msbuild into an open command prompt:

>msbuild
'msbuild' is not recognized as an internal or external command,
operable program or batch file.

So I added MSBuild.exe to my path (C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe) and opened a new command prompt. To be certain, I printed out my new path:

>echo %PATH%
[...];C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe

Excellent, and now to try using msbuild:

>msbuild
Microsoft (R) Build Engine version 4.0.30319.17929
[Microsoft .NET Framework, version 4.0.30319.17929]
Copyright (C) Microsoft Corporation. All rights reserved.

MSBUILD : error MSB1003: Specify a project or solution file. The current working 
directory does not contain a project or solution file.

Great, that’s to be expected.

Now back to Jenkins. I added the command I expected to run as a build step for my job (specifically: ‘execute Windows batch command’) like so:

I’d already tried this command locally and it worked great. However, the Jenkins job did not like this at all and gave me the same error as before:

'msbuild' is not recognized as an internal or external command,
operable program or batch file.

I did some Googling around and there is actually an MSBuild Jenkins plugin which comes to the rescue.

I installed it, configured it in Jenkins > Manage Jenkins.

Then I replaced the old build step with the new one.

The build now runs successfully in Jenkins :-)

Mar 31

Highlighting Elements in Selenium WebDriver

I’ve decided to write smaller blog entries for a while as I have such a huge backlog of things I’d like to put on here.

So today I’d like to introduce how to highlight elements in the browser using Selenium WebDriver (Java).

Rather than simply explaining how it’s done and giving some code snippets, I’ve also included the functionality in a small Java project that uses my favourite test runner – JBehave. This way, you can build the project yourself, see a “real-life” example and tinker with it.

I’d like to thank Iain for his blog post on highlighting elements.

Getting Started

First of all – if you are building an automation framework at your organisation – it’s a good idea to route all calls to the WebDriver API through a bespoke common library. I’ve discussed at length why I think this is a good idea but here’s a few more reasons:

  • It helps enforce standards inside your organisation. Flexibility is a good thing but do you really want people using your framework to select elements using findElement.By.partialLinkText rather than an Xpath or CSS Selector? A restrictive approach does not necessarily stifle innovation.
  • It can vastly reduce code.
  • It increases code readability.
  • Particularly difficult things to automate (e.g. hover and click, drag and drop) can be used again and again by different project teams.
  • A solid common library makes tests more stable as it is harder to introduce silly errors into your code.
  • It is much much easier to update common methods with the latest API additions and best practise.

We need two methods to achieve our aim.

First, we need to create a setAttribute() method similar to the WebDriver getAttribute() method. The WebDriver API does not have this built in because it is specifically designed to mimic the user’s interactions with the browser – and the user cannot change attribute values (through “normal” use).

The definition of this method depends on your setup. You need to cast your WebDriver instance to JavaScriptExecutor for it to work.

If you use JBehave like I do then WebDriverPage casts the WebDriver instance to JavaScriptExecutor for you already. Like so:

    public Object executeScript(String s, Object... args) {
        makeNonLazy();
        return ((JavascriptExecutor) webDriver()).executeScript(s, args);
    }

So we can call executeScript() directly in our method without worrying about casting it first.

  /**
   * Set an attribute in the HTML of a page.
   * 
   * @param element
   *          The WebElement to modify
   * @param attributeName
   *          The attribute to modify
   * @param value
   *          The value to set
   */
  private void setAttribute(WebElement element, String attributeName, String value) {
    executeScript("arguments[0].setAttribute(arguments[1], arguments[2])", element, attributeName, value);
  }

If you don’t use JBehave then this should do the trick:

  /**
   * Set an attribute in the HTML of a page.
   * 
   * @param driver 
   *          The WebDriver you are using
   * @param element
   *          The WebElement to modify
   * @param attributeName
   *          The attribute to modify
   * @param value
   *          The value to set
   */
  private void setAttribute(WebDriver driver, WebElement element, String attributeName, String value) {
    JavascriptExecutor js = (JavascriptExecutor) driver;
    js.executeScript("arguments[0].setAttribute(arguments[1], arguments[2])", element, attributeName, value);
  }

Now we just need to create a highlight method that uses setAttribute().

  /**
   * Highlight an element in the UI.
   * 
   * @param element
   *          The WebElement to highlight
   */
  private void highlight(WebElement element) {
    final int wait = 75;
    String originalStyle = element.getAttribute("style");
    try {
      setAttribute(element, "style",
          "color: yellow; border: 5px solid yellow; background-color: black;");
      Thread.sleep(wait);
      setAttribute(element, "style",
          "color: black; border: 5px solid black; background-color: yellow;");
      Thread.sleep(wait);
      setAttribute(element, "style",
          "color: yellow; border: 5px solid yellow; background-color: black;");
      Thread.sleep(wait);
      setAttribute(element, "style",
          "color: black; border: 5px solid black; background-color: yellow;");
      Thread.sleep(wait);
      setAttribute(element, "style", originalStyle);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

It’s not the prettiest code but the effect is pretty cool.

You could parametrise the colours if you wanted to. I also feel that the wait time is enough to clearly notice what’s going on without slowing tests down.

Using it in your code.

OK so let’s see the code in action.

As I mentioned earlier, I built a small Java project for this which is available at my public GitHub repository. You just need Maven and Firefox installed.

Simply follow the running instructions to see the highlight method in action while searching for things on Amazon.

Feb 06

Building Powerful and Reliable WebDriver Automated Tests without Learning the API

I’d like to take a quick break from the technical side of things in order to introduce this particular topic which I strongly believe in.

One of the most common complaints surrounding automation relates to learning the API (in this case Selenium WebDriver). Less confident programmers (usually testers) will usually confuse the host language (e.g. Java) and the automation API (Selenium WebDriver) and fail to understand where one begins and the other ends. Similarly – from my experience – experienced Java developers will view the API with suspicion and complain that “they don’t have enough time” to learn it or need some sort of training in order to do so.

There is some truth in the latter statement. I’ve found it quite difficult at times to find really solid advice and examples with regard to Selenium WebDriver (one of the reasons why I started this blog). So let’s put this statement out there:

It is possible (and so easy!) to write your tests in such a way that the user does not need to know ANYTHING about Selenium WebDriver and how it works or why.

One of the things I love about WebDriver is that it so fast and powerful. But as Peter Parker would say, with great power comes great responsibility…or something like that at least. WebDriver is utterly unforgiving if you don’t respect it. By this, I am of course talking about implicit/explicit waits – or rather WebDriver falling over in a heap if it can’t act upon some element that you haven’t told it to wait for.

So what do I propose?

A simple solution really. I assume you’re currently using the Page Object pattern. If not, take a step back and learn this as it’s a fundamental technique which contributes to solid tests. Let’s have a look at a fictional example:

public class HomePage extends WebDriverPage {

private static final String SEARCH_FIELD = "//*[@id='search']";
private static final String SUBMIT = "//*[@id='submit']";

    public HomePage(WebDriverProvider driverProvider) {
        super(driverProvider);
    }


    public void go() {
        get("http://www.bensnape.com");
    }


    public void search(String content) {
        findElement(By.xpath(SEARCH_FIELD)).sendKeys(content);
        findElement(By.xpath(SUBMIT)).click();
    }
}

Let’s assume this page object represents www.bensnape.com and allows you to navigate to the page – go() and do a search using search(). Now the code for this looks fine, right? But what if in our automated test we navigated to this page via a link and not via the blocking WebDriver get() before trying to call the search() method. What happens? WebDriver falls over because the page hasn’t finished loading.

The typical reaction would be to put some sort of wait (implicit or explicit) at the start of the search() method. However, this is bad practise for the following reasons:

  1. It would be very difficult to anticipate how your page objects might be used in further iterations of your project, or longer term by other projects in related areas.
  2. Putting waits everywhere would clutter your code and hamper readability.

The solution is to introduce a class or set of classes to extend all your page objects from which contain easy-to-use, re-usable and efficient helper methods. Let’s implement that class now:

public class CommonMethods extends WebDriverPage {

    public CommonMethods(WebDriverProvider driverProvider) {
        super(driverProvider);
    }

    public void clickLink(String xpath) {
        WebElement link = new WebDriverWait(webDriver(), 10).until(ExpectedConditions.elementToBeClickable(By.xpath(xpath)));
		
	link.click();
    }
	
    public void fillField(String xpath, String content) {
	WebElement field = new WebDriverWait(webDriver(), wait).until(ExpectedConditions.visibilityOfElementLocated(By.xpath(xpath)));
		
	field.sendKeys(content);
    }
	
}

And so we extend that common class to wrap all our WebDriver API calls with targeted and explicit waits related only to the element we are interacting with in clean, easy to understand methods of our own choosing – namely clickLink() and fillField().

public class HomePage extends CommonMethods {

private static final String SEARCH_FIELD = "//*[@id='search']";
private static final String SUBMIT = "//*[@id='submit']";

    public HomePage(WebDriverProvider driverProvider) {
        super(driverProvider);
    }

    public void go() {
        get("http://www.bensnape.com");
    }

    public void search(String content) {
        fillField(SEARCH_FIELD, content);
        clickLink(SUBMIT);
    }
}

I introduced this simple but powerful concept at Yell. It enables less experienced programmers to easily perform complicated API calls without needing to learn the WebDriver API at all. By simply abstracting the technical implementation inside easy-to-understand business language – e.g. clickLink, fillField, selectFromDropdown etc. code readability, durability and stability vastly improve, along with testers’ output and confidence levels.

Dec 18

Using the Advanced User Interactions API in Selenium 2 (Webdriver)

I was recently automating an internal sales tool used at Yell when I came across this particular problem. I needed to hover over the navigation bar and then click on one of the options that drops down (see Play.com for a working example).

This requires use of Selenium’s Advanced User Interactions API – which was new to me – but this was only the start of my problems as we will discover.

The scenario

We have some HTML that looks like this:

Note: this probably isn’t syntactically correct but you can see that the navigation bar is an unordered list of unordered lists. The dropdown menu is initially hidden from view (display:none).

<div id="navBar">
 <ul>
  <li>
   <a href="http://customer"> My Customers </a>
    <ul id="dropDownMenu" style="display:none;">
     <li>
      <a id="searchCustomer" href="http://searchcustomer">Search</a>
     </li>
…

Once you hover over the main link, the HTML transforms, exposing the hidden links (display:block):

<div id="navBar">
 <ul>
  <li>
   <a href="http://customer"> My Customers </a>;
    <ul id="dropDownMenu" style="display:block;">
     <li>
      <a id="searchCustomer" href="http://searchcustomer">Search</a>
     </li>
…

Start creating a solution

I wanted to solve this problem and at the same time create a helper method for the framework that was flexible, powerful and reusable. The method declaration would look something like this:

public void clickHiddenMenuBarItem(String menuXpath, String menuItemXpath, long timeout)

In the interest of best practice, I try to ensure that all tests make frequent use of helper methods which typically include targeted explicit waits. That way tests are much more readable with the complexity hidden away and it also reduces the likelihood of testers making those simple but frustrating programming errors.

So clickHiddenMenuBarItem as the name implies, would click on a hidden menu bar link which must be revealed by hovering over the menu link first.

So let’s build two explicit waits for the main link and the hidden link (note: the latter is hidden from the user and Selenium’s direct interactions but you can still verify its existence).

NB: due to my setup (I use Spring to run my code), the Selenium commands are missing the prefix driver.. Similarly, you would need to use driver instead of webDriver() for your WebDriver instance (or whatever you named it).

  public void clickHiddenMenuBarItem(final String menuXpath, final String menuItemXpath, long timeout) {

    // Explicit wait for the menu
    WebElement menu = (new WebDriverWait(webDriver(), timeout)).until(new ExpectedCondition() {
      public WebElement apply(WebDriver d) {
        return d.findElement(By.xpath(menuXpath));
      }
    });

    // Explicit wait for the menu option
    WebElement menuOption = (new WebDriverWait(webDriver(), timeout)).until(new ExpectedCondition() {
      public WebElement apply(WebDriver d) {
        return d.findElement(By.xpath(menuItemXpath));
      }
    });
  }

These are your standard explicit waits as defined by the documentation. As each wait makes use of an inner class we must declare the xpath arguments as final.

You might want to extend the piece of code above to check the visibility of the elements, as this helped me to diagnose problems later on:

System.out.println("Menu displayed: " + menu.isDisplayed());
System.out.println("Menu option displayed: " + menuOption.isDisplayed());

This should return true and false respectively.

Introducing the Advanced User Interactions API

Now that piece of code is working fine, we can move onto making use of the Advanced User Interactions API. This is where I got a little stuck and where isDisplayed() came in handy.

According to the documentation you should be able to do something like this:

// Configure the action
Actions builder = new Actions(webDriver());

builder.moveToElement(menu)
.click(menuOption);

// Get the action
Action clickMenuOption = builder.build();

// Perform the action
clickMenuOption.perform();

This compiled fine for me, ran without errors but nothing seemed to happen. The mouse didn’t hover on the menu, even when I stepped through it using the debugger. So I tried a variant on this, which is available from the same link above:

// Configure the action
Actions builder = new Actions(webDriver());

// Get and build the action
Action clickMenuOption = builder.moveToElement(menu)
.click(menuOption)
.build();

// Perform the action
clickMenuOption.perform();

Again nothing was happening. After some searching around I found that someone had had a related problem. Essentially, you need to perform the click() outside of the action chain. So here’s the code:

Actions builder = new Actions(webDriver());

builder.moveToElement(menu).perform();

menuOption.click();

I tried this…and it almost worked. The mouse certainly hovered on the link but the code fell over as there is a tiny (<500ms) delay where the menu option becomes 'un-hidden'. To achieve this diagnosis I put the earlier isDisplayed() code along with a small static wait in-between builder.moveToElement(menu).perform() and menuOption.click()

Now I knew what the problem was I needed to find a neat and tidy way of structuring this wait.

Using the ExpectedConditions class

This question of a neat and tidy wait had me stumped for some time. I knew that you could use an explicit wait whilst waiting for the existence of an element – but I had already proved that in my previous code. I needed a way of somehow combining the visibility of an element with a wait (and importantly, elegantly).

An implicit wait for 1 or 2 seconds worked – manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS) but it is bad practise to combine explicit and implicit waits. Besides, the implicit wait would live for the life of the WebDriver instance which is unacceptable. I almost resorted to Thread.sleep(500) to achieve the desired effect. Finally, I found the Selenium ExpectedConditions class. I needed to know when, after hovering on the menu, the menu option became visible and clickable – and elementToBeClickable was a perfect match. So simply put that condition as part of a WebDriverWait statement like so:

Actions builder = new Actions(webDriver());
builder.moveToElement(menu).perform(); // hover

new WebDriverWait(webDriver(), 10).until(ExpectedConditions.elementToBeClickable(By.xpath(menuItemXpath)));
menuOption.click();

Test the solution

This code worked for my application but when I tested it on Play.com – which has a very similar navigation bar – it didn’t work. I had a hunch that my code was failing due to being “too slow” in identifying when the hidden element was clickable and acting upon it.

There are two possible solutions:

  1. Use a different WebDriverWait constructor to specify the element polling interval (measured in milliseconds).
  2. new WebDriverWait(webDriver(), 10, 50).until(ExpectedConditions.elementToBeClickable(By.xpath(menuItemXpath)));
    
  3. Use a FluentWait which works similarly to WebDriverWait (which in fact extends FluentWait) but gives you a little more flexibility.
  4. new FluentWait(webDriver())
    .withTimeout(timeout, TimeUnit.SECONDS)
    .pollingEvery(50, TimeUnit.MILLISECONDS)
    .ignoring(NoSuchElementException.class)
    .until(ExpectedConditions.elementToBeClickable(By.xpath(menuItemXpath)));
    

Whilst the FluentWait offers more flexibility – in particular the ability to choose the WebDriver exception to ignore – in this case I opted for the more finely-tuned WebDriverWait as it achieved what I needed in one line of code.

Full code

  /**
   * Click a hidden menu bar item (AKA hover and click) using a configurable wait.
   * 
   * @param menuXpath
   *          The xpath of the main menu parent item.
   * @param menuItemXpath
   *          The xpath of the hidden/drop-down menu item.
   * @param timeout
   *          The wait timeout (10 is recommended).
   */
  public void clickHiddenMenuBarItem(final String menuXpath, final String menuItemXpath, long timeout) {

    // Explicit wait for up to 10 seconds for the menu
    WebElement menu = (new WebDriverWait(webDriver(), timeout)).until(new ExpectedCondition<WebElement>() {
      public WebElement apply(WebDriver d) {
        return d.findElement(By.xpath(menuXpath));
      }
    });

    // Explicit wait for up to 10 seconds for the menu option
    WebElement menuOption = (new WebDriverWait(webDriver(), timeout)).until(new ExpectedCondition<WebElement>() {
      public WebElement apply(WebDriver d) {
        return d.findElement(By.xpath(menuItemXpath));
      }
    });

    /*
     * Use the Advanced User Interactions API to perform a hover and click.
     * A WebDriverWait utilising elementToBeClickable() is required between the hover and click.
     */
    Actions builder = new Actions(webDriver());
    builder.moveToElement(menu).perform();  // hover

    new WebDriverWait(webDriver(), 10, 50).until(ExpectedConditions.elementToBeClickable(By.xpath(menuItemXpath)));

    menuOption.click();
  }