Friday, October 21, 2011

Monkey Patching Firefox Race Conditions For Watir-WebDriver/Cucumber

If your team is like my team, you're trying to find ways to automate everything. Perhaps you're also using watir-webdriver to run browser based tests with Cucumber. This works great locally, but when you try to run your tests on a CI environment you start to run into some very strange problems with Firefox testing.  You start to get errors around not being able to find the h1 tag on your page.  You look at the screen shot you've generated using a method similar to this, and you find that your screenshot shows the text that contains the h1.  In fact, your error dumped html file also shows the h1 tag:

You've run into selenium issue 2099.  It appears what happens with this error is that the test is executing before the page has fully loaded, causing it to fail.  Old school race conditions.  Nice.

So how do we get around this?  There's been a few postings on how to handle this with Capybara, but what about tools that use selenium-webdriver more directly?  Here's a monkey patch that hacks over the Ruby bindings to enable tools like watir-webdriver to retry.  This patch will retry the find until selenium-webdriver receives an error that is not related to the strange InvalidSelectorError that it gets when the Firefox isn't done loading the page. I just stuff this file into the support directory in a Cucumber suite.

Since this is hacky and a monkey patch, I'm very keen on ideas on how to improve this implementation, since my Ruby skills are very raw.

Thursday, September 22, 2011

Integrating jasmine-maven-plugin With Your Legacy Enterprisey App

Let's say you're working on enhancing an old legacy app with javascript. Doesn't really matter what sort of app, just legacy, with an old source tree and an old deployment style. How can you integrate jasmine within that app and have it also integrate with your new fangled CI environment? What if you've also been asked to use maven since it's "The way forward" for your organization? How can you accomplish this?

Well, there's the jasmine-maven-plugin, which is all sorts of awesome, but because you have a legacy file structure there's no clear way on how to hook it into your legacy app. But when there's a will there's a way.

There are several problems that need to be tackled here. Let's go over them bit by bit.

First, how do we get jasmine-maven-plugin to see our legacy app?

Let's hope that you can create your project somewhere "near" the legacy app in the SCM system. In this example, I'm going to assume that we have a tree structure that looks like this:

Pretty simple, we'll put our specs inside jasmine-tests\src\test\javascript. We'll put any jasmine required javascript inside jasmine-tests\src\test\javascript\lib. We just need to figure out how to tell jasmine-maven-plugin to look at our legacy app for our scripts. Fortunately, it's configurable. Let's look at a snippet of the pom.xml that shows how you would configure the plugin.

In this configuration, we're telling the jasmine-maven-plugin to look outside of our project structure for our javascript files. It even preloads our new fangled jquery thing that we've introduced into our legacy application from our legacy.js.src property. The key to this is that we've kept the configuration of the plugin outside of our execution configuration. When jasmine-maven-plugin runs, maven will actually fork a process to generate the manual runner. It will not receive the configuration settings if you're using an older maven like 2.0.11. I do not know if 2.2.1 or 3.x change this. By placing the configuration at the top level of the plugin, we fix that concern.

At this point, we actually have a plugin that can execute on our system if we've checked out our entire source tree. It should also execute in your CI environment.

However, some teams, like to check things out in different directory structures than what's in the SCM tree due to their tools (like Eclipse). Although your team is excited, they've also noticed that the ManualTestRunner is deprecated. When they try to run jasmine:bdd, they find that nothing gets picked up from the legacy app because the jetty runs from the root of your jasmine-tests directory. It can't see things underneath it. Normally you'd create a symbolic link,'re on Windows. Windows XP. Remember, I said Enterprisey app, not just legacy.

How can you fix the process so you can run jasmine:bdd like good sensible developers? Glad you asked. Maven profiles to the rescue. And, a neat plugin that lets you build symbolic links on crazy things like Windows XP.

Let's look at the profile:

The maven-junction-plugin actually builds symbolic links in both unix and windows. We redefine the location of our legacy.js.src to be a temporary directory inside the jasmine-tests project. The checkedout.legacy.js.src points to where our application has been checked out locally. The plugin is executed in the initialize phase so it occurs before jasmine. And initialize is supposed to setup directory structures, and the like, so it makes sense.

Since we've redefined the legacy.js.src, jasmine:bdd will simply see our files as if they are inside the project, and the jetty will be able to serve them properly.

Our final pom looks like this:

Hopefully this is helpful for you.

Monday, August 01, 2011

The Problem with PageObjects, why PageParts are needed

Over the past year or so, several big dogs in the automated web testing community have been pushing hard with the concept of PageObjects. This is a great thing. As such we now have at least two different page object frameworks to use with Watir-Webdriver thanks to the efforts of Alister Scott and Cheezy.

However, the page object frameworks have placed an item on the backburner. I'm not sure why it is, but the concept of repeating items inside of a page has been ignored at this point. Let's say you have a website like Google Finance. The list of news items is a great example of a what I'm calling a PagePart.

PageParts are items that repeat themselves on a page in terms of content types. They repeat with different content depending upon the user who is logged in, but they all look the same semantically. Here's what a Google Finance News item looks like:

There are four of these items wrapped in a div with market-news-stream as it's id.

Without PageParts you might do this:

So how do we identify these items in the tests? When I first started tackling this sort of problem on my current project I had pass through methods inside my PageObject that went to a simple PagePart. This worked, but didn't give me the cool features of the PageObject frameworks, and wasn't that the whole point of adopting the framework?

Wow, this is more complex than not using page parts.

So, how exactly is this better? It's better when you convert your page parts to use the PageObject framework. Caveat, I do not know how you would do this in Cheezy's PageObjects gem, but it works just fine in the watir-page-helper gem:

Every PagePart extends the BasePart. You'll select your container div in your PagePart object using the part_div method. Obviously this could be any HTML element, but it's most likely to be a div. Since the BasePart will redefine @browser with the div, we'll be able to use the cool shorthand, rather than define every method we might need:

The example may seem a bit contrived, but, maybe you're not working with an interface as clean as Google Finance. If you are, you might get some benefit out of using PageParts.