Friday, February 22, 2013

PRACTICUM: Selenium 2 Testing Tools Beginner's Guide: Selenium Grid

This is the next installment (completed) of my PRACTICUM series. This particular grouping is going through David Burns' "Selenium 2 Testing Tools Beginner's Guide".

Note: PRACTICUM is a continuing series in what I refer to as a somewhat "Live Blog" format. Sections may vary in time to complete. Some may go fast. Some may take much more time to get through. Updates will be daily, they may be more frequent. Feel free to click refresh to get the latest version at any given time. If you see "End of Section" at the bottom of the post, you will know that this entry is finished :).

[Note for 2/19/2013: Compiler errors... no, that will not stand, man! I will get to the bottom of this... and I think I finally have. For those who are using Java 7 as your JDK, look towards the bottom, I discovered something that resolved our past compile problems, or at least, I think I have :). --MKL].

Chapter 8: Selenium Grid

One of the nice things I have noticed thus far with regards to the Selenium tests that I have seen is that they are relatively easy to change so that the tests can be run on multiple devices. The down side is that, so far, we have had to run them all one at a time, or switch things over to different servers, different bridges, and check them out one at a time. Also, tests are launched serially and run in order in the approaches we have taken so far. That's all cool, but is there some way that we could simplify things and just have one place we could focus our attention? The answer is "well, almost!"

Selenium Grid lets us set up a number of Selenium instances, with one central "hub" to send commands to. Selenium Grid routes the commands destined for a particular device or driver based on the configuration options and identification elements we create. While it's not an "all in one" solution, it's a nice way to go about gathering a lot of tests under one roof and varying the ability to test lots of different devices. First, though, we need to set up our environment to allow for a grid server.

Starting up Selenium Grid

Thankfully, this is an easy first step. We start up Selenium Server like we usually do, but we add a flag to it, like this:

java -jar selenium-server-standalone-2.29.0.jar -role hub

This will start up selenium server in grid mode (note, of course, use the version of selenium-server-standalone[x.xx.x].jar that you have, if you have a newer version, it should work the same).

And here's what your browser will say if you point to the server.

Cool, we've successfully started Selenium Grid. All looks good here. This is going to be the center point of our tests, and other Selenium instances will be reached from here. When we start Selenium Grid, we can see what options we have control over and which ones we can change. By passing in the port number (i.e. –port ####) We can use any value that we want (and have access to, of course).

By typing in


we can see what environments are accessible (or at least assessible as far as the grid hub is concerned).

Unlike the Selenium Grid 1.0 version, 2.0 allows a machine to host multiple servers and allow access to multiple browser drivers.

Setting up Selenium Grid with a Local Server Node

This time, we'll look at starting up Selenium Grid, as well as starting up a second Selenium server.

Enter the following command in the apropriate location (note, the previous selenium grid command needs to be running for this to work. That's not directly mentioned in the book):

Start with

java -jar selenium-server-standalone-2.29.0.jar -role hub

and then run

java -jar selenium-server-standalone..jar -role node -hub http://localhost:4444/grid/register

You should see the following in your command prompt or console:

And if all goes well, this is our Selenium Grid display:

Nice! Looks good :)!

Setting up Selenium Grid With Remote Server Nodes

While having the grid running locally is cool, to really leverage the benefits of Selenium grid, what we really want to be able to do is run Selenium Server on multiple machines, and have those machine register their connections with the main Selenium Grid hub. The more machines we have that can do this (and the different variety of machines we use for this purpose), the more we can leverage the benefits of cross-platform and parallel testing. For this purpose, here's my setup with a second Darwin device and my Windows machine:

Each of the machines needs to be registered, and each needs to point to the primary grid hub, like this:

1. Make sure the hub system is running on my main Darwin system:

 java -jar selenium-server-standalone-2.29.0.jar -role hub

2. Set up a secondary local Darwin Selenium server instance and have it pointing to the hub:

java -jar selenium-server-standalone-2.29.0.jar -role node -hub http://localhost:4444/grid/register

3. Set up the second Darwin machine to run a Selenium server and register it with the first Darwin machine's hub:

java -jar selenium-server-standalone..jar -role node -hub

4. Set up the Windows machine to run a Selenium server and register it with the first Darwin machine's hub:

java -jar selenium-server-standalone..jar -role node -hub

Cool, so if we did that all correctly, we should have a grid with three registry entries... do we?

Indeed we do. Right on! So the key here is that we have the hub instance running, and with that, we can set up as many remote environments as we have machines. For our purposes, though, I think this will be sufficient to make the point.

Setting up Selenium Grid to dedicate OS/Browser Specific Tests

If we only needed to run on one browser and one operating system, this would be a much easier situation to deal with. In that case, much of Selenium Grid would be to merely provide for parallel tests and pointing to different instances of Selenium Server, all utilizing the same browser driver. While that's probably OK in some instances, most of us have to deal with situations that require a variety of browsers. More to the point, we don't have to just consider issues of browsers (Chrome, Firefox, Opera) in their own rights, we also have to consider the challenges of running them in a variety of Operating Systems (Windows, Mac, Linux, etc.) Even with Internet Explorer being solely on Windows, there are now multiple versions and multiple OS considerations (XP, Vista, 7, 8). Safari runs on Mac, and we haven't even started talking about Mobile yet! Selenium Grid, in a manner of speaking, gives us "One Ring (Hub?) to Rule Them All!" Well, OK, perhaps not all, but there are nine options that we can leverage at this point.

We've already seen that we can set up a base hub for the Grid, and use the -hub flag to point remote instances to it. We can use the -role flag to associate remote instances of Selenium server with our Grid hub. To get the browser level into the mix, we use a flag called -browser . The following command lets up set up a Selenium Server that registers with Internet Explorer specific options:

java \
-jar selenium-server-standalone.jar \
-role node \
-hub http://localhost:4444/grid/register \
-browser browserName="internet explorer",maxInstances=1,platform=WINDOWS

If this is doing what it's supposed to do, then we will have a Grid entry that only references Windows. That's what the book shows. My reality is a different story:

Not sure if that's going to make a big difference, but suffice it to say, we're running with the parameters the book told us to run. In our later tests, we'll see if they run correctly.

Using Selenium Grid 2 with YAML

YAML is a way that we can take commonly used data and encapsulate it into a file, so that the details we might want to update, or run with several instances, can be sourced from one place. Here's the suggested example for our YAML file (called grid_configuration.yml):

There are two different configuration options that are available. If you have Selenium 1 Gris servers (i.e. Selenium RC) and you want to run them along with the more up to date Selenium2/WebDriver servers, you can. Define the details you want to configure in the YAML file, and when you start up the Grid hub, give it the flag -grid1yml and the name of the YAML file, and you're good to go. 

A  quick look at the config options shows us our grid1yml file configuration options (note that we haven't created a grid 2 configuration file yet):

The key takeaway here is that a lot of the parameters that we would want to keep track of can be housed in a couple of YAML files, and that makes configuration changes quite a bit easier to track.

Actually Running Tests in the Grid

Up to this point, I have to say that my update speed has been very slow with this chapter. That's been a combination of work conspiring against me, plus some unusual issues with getting files to compile that made me decided that I had to get to the bottom of it. Through that process, i discovered that Java 7 requires a little more verbosity than Java 6 did. I could be wrong about this, but on the bright side, I think I may have found an answer to the issues of not being able to compile in previous sections.

First, let's talk running tests that leverage the grid. This is a simple example, simply because I decided I didn't want to fight multiple machines, multiple drivers, and multiple error messages just yet (hey, I'm having such fun with it, why shouldn't I let you all do the same ;) ?). But, to show that we are actually "gridding" it, here's a simple test that I put together to make sure that it goes to a particular location. In the example below, I have three servers. One's running on Windows, one's running on Linux and one is running on Mac. I want to make sure the test in question goes to the Mac instance. 

Right, so what's interesting about the above code? Glad you asked. As I was setting this example up, I kept getting the compile error "SetPlatform requires Platform, but found String".

This is so similar to all of the design pattern errors I was getting, I decided that there had to be a reason I'm seeing this. Additionally, I've gotten to the point that I couldn't believe that so many code examples were just not workable. I have met David, and I know he's way more conscientious about this stuff than to allow so many "mistakes". Well, what if it's not a mistake? What if Java 7 requires stricter type usage? As soon as I thought about that, and remembered back in my old C days that I could "force" a type casting to be applied, I started experimenting. In the process I came up with the following approach and change (note the difference between the two files):

Once we forced the type requirement, the project compiled! Not only that, but it actually ran the test to the specific server we wanted to have run it. Look below, the grid console shows the test instance being run. It's the muted firefox icon, and yes, that server is indeed the one running on my Mac:

Oh, and note... I finally got my Windows Server to declare itself an Internet Explorer only node, so yes, it's doable. I don't know what I did differently other than have the Windows version run the latest version of Selenium Server... hmmm, that might be it.


Yeah, this section took a long time to get through, but on the plus side, I made a neat discovery, and that discovery may go a long way to solving a few other problems. Net result is that I'm going to go back and do some re-testing and re-coding of the previous examples and bring them in line with Java 7 realities. Seriously, I am ecstatic that there is a potential solution to all of the random compile errors, and if it's this simple, then there will be some easy changes to suggest to Packt so that they can perhaps make an update in the errata to reference Java 7 type conversion (or lack thereof :) ).

End of Section.

No comments: