Monday, December 24, 2012

Seeing "Info" Messages in a Log Does Not Automatically Imply "Error"

Here's what seems to be a common question among Java language bindings users of the IE driver (tl;dr):

Whenever I use the IE driver, I see an error in my log. The log output is below. How do I stop it?
Started InternetExplorerDriver server (32-bit)
Listening on port 48630
Sep 23, 2012 9:12:21 AM org.apache.http.impl.client.DefaultRequestDirector tryExecute
INFO: I/O exception (org.apache.http.NoHttpResponseException) caught when processing request: The target server failed to respond
Sep 23, 2012 9:12:21 AM org.apache.http.impl.client.DefaultRequestDirector tryExecute
INFO: Retrying request

Follow-up questions like, "What happens after you receive this message?" or, "Does the rest of your code run?" usually get a response like, "No, the rest of my code works fine with IE, I just see this error message, and I don't think I should be seeing any errors."

There are two answers to this question, a short one and a long one. The short one is, "Read the log message. It's clearly tagged as 'INFO', as in an informational message, and not indicative of any problem with the code?" I find that this question often comes from users of Eclipse, and that the Eclipse console has colored the message red, and people are so conditioned to see "red == bad" that they react to the format of the message rather than the content. The content of the message is flagged at a level that means, "Hey, nothing is wrong, we're just telling you about it."

If all you care about is how to get around the message, you can stop reading here. The longer answer to the question involves an explanation of why you might receive this message. The explanation requires a little knowledge of the architecture of the WebDriver language bindings in general, and the IE driver in particular. Remember that in the language bindings for most browsers, communication with the browser happens via a "server component", and the language bindings use a JSON-over-HTTP protocol to communicate with that server component. The server component could be an instance of a remote WebDriver server (whether written in Java, or another language); or it could be the Firefox browser extension that the FirefoxDriver installs into an anonymous profile and uses; or it could be an instance of chromedriver.exe; or, and this is the important bit for our purposes here, it could be an instance of IEDriverServer.exe. Each of these server components starts up an HTTP server with which the language bindings can communicate.

Let's think about what happens when you ask the Java language bindings to create a new instance of the InternetExplorerDriver class.

  1. Locate the IEDriverServer.exe, either from the system PATH or from a Java system property
  2. Launch IEDriverServer.exe, including its internal HTTP server
  3. Use the org.apache.http.client.HttpClient class to send HTTP requests to the IEDriverServer's internal HTTP server to launch IE and establish a WebDriver session

Now looking at those steps, can you see the potential flaw in the execution? If you said "race condition," you earn a gold star. The Java method for launching a process will return before the IEDriverServer's HTTP server has had a chance to fully initialize itself and be ready to receive HTTP requests. However, since IEDriverServer.exe is running in a separate process, there's no way for the Java language bindings to know when the HTTP server has fully initialized. The only way to handle the situation is to poll for the HTTP server, repeatedly sending requests until you get a valid response, and that's the message you're seeing in the log. All the bindings are telling you is that the HTTP server hasn't yet been fully initialized, so it's trying the request again, nothing to see here, move along.

The bottom line is that the message is informational, not indicative of an error, or else it would have a different logging level. Remember, the format of a log message is incidental; the important thing is its content.

Wednesday, December 5, 2012

Not Providing an HTML Page? Think of the Kittens!

Consider the following request for help with Selenium WebDriver:
I'm having trouble getting something working in WebDriver. I'm getting a NoSuchElementException when I try to execute the following code:
What could be causing this exception, and how do I fix it?
Obviously, this code could have several potential problems. The ID attribute of the element may be wrong. The element the user is looking for could be inside a frame or iframe. The element may be added to the page DOM via some JavaScript library, and the browser hasn't had time to process the JavaScript yet when the findElement call is made. Or it could be something else. What's missing from this request for help, whether the request is made in a post to one of the user-facing mailing lists, or in an issue report, is context. Anyone reading the request won't know the structure of the page the user is automating, so any attempt at help is going to be pure speculation.

Most of the time, people wanting to help the requester will respond with something along the lines of, "Hey, we really can't give you any guidance unless we can see a page that reproduces the problem. Can you share the page, or a URL to a page that will demonstrate the issue?" Surprisingly often, this simple request for more context is met with a very large amount of resistance, along with reasons why it isn't possible. Almost equally as often, these reasons are bogus. Let's look at a few of these reasons, and see why they're bogus.

Evans Bogus Reason 1: "It's hosted on an intranet."

Just because a webpage happens to be hosted on an intranet doesn't mean it can't be shared. Yes, it will require some effort on your part to make it public. Such applications usually rely on some sort of database connection, or connection to internal resources, or LDAP integration, or some other such thing. Here's a little secret: Because WebDriver works only on the UI, your WebDriver problem most likely has nothing whatsoever to do with those underlying technologies. Yes, that means you'll probably have to stub out anything like that your application relies on before you can make it public, but it can be done.

Evans Bogus Reason 2: "We're so afraid someone's gonna steal our super-secret site concept/source code"

This reason is often imposed from on high by an upper management drone. It's penny-wise and pound-foolish. You're paying someone good money to develop automated code against your web application. The people you're paying are encountering difficulty to the degree that they need outside help to figure out why. Yet you're so scared that someone is going to steal your precious code or idea that you refuse to let them get help, and causing that money to be wasted because your automators aren't being productive.

I direct this next bit to those short-sighted nincompoops who refuse to get out of their people's way. If you're going to insist on making it hard on your WebDriver coders who are caught between a complex web application and your tunnel-vision, I'll make an offer here and now to sign whatever NDA is required to get them some help; however, I'll charge a fee for it. A big one. Much, much more than if you had just let your coders get the help they need in the first place.

Evans Bogus Reason 3: "Do you know how complicated my site's JS framework/CSS/HTML code is?"

The snarky jerk in me always wants to respond to this reason with something like, "Well, no, I don't know how complicated it is because I can't see your code from here, but it's highly likely that complexity is exactly your problem." Simplify the page down to plain, vanilla HTML if you can. It's not like WebDriver is entirely untested. There's a CI server that runs a bunch of tests (600+ at the time of this writing) on every single commit, against 5 version of IE, 4 versions of Firefox, Chrome, and Opera. If you're doing something reasonably common in your WebDriver code, it's probably a safe bet that it's not going to be globally broken for everybody, everywhere, against every web page.

Also, there's no requirement that someone needs to use the exact same site that's giving them the problem. One that is similar will do the trick, which is why we who try to provide help often say ask for "an HTML page, or a public URL to a page" that demonstrates the problem. Here's a tip, most of the JavaScript UI frameworks provide public demo pages. See if you can replicate the behavior from your WebDriver code against the demo page.


Evans Bogus Reason 4: "But I'm just a lowly tester, and I'm not allowed to touch the application code"

I can't believe this is actually put forth as a reason, since the solutions are many and obvious. Work on a private fork of the application code. Enlist a developer to help you. Try to reproduce your problem against a different, public site.

Remember, We're All Volunteers Here

The people who develop and provide support for WebDriver are unpaid volunteers. If you're having an issue with running WebDriver against your web application, and you need to ask for help why it's failing for you, it'll go a long way toward establishing goodwill (and getting you a productive answer) if you provide everything someone needs to reproduce your problem easily.