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 :).
Chapter 4: Design Patterns
For anyone that has ever recorded a test, with the hope of going through later and "simplifying" it so that they can use it for another purpose, you know the pain of which I speak. The trashing of 90% of the material, the trying to shoe-horn what appeared onto the screen into some kind of "less than brittle" fashion, the endless rework to make it all do something reliably, and then the praying that it just holds together. Sound familiar?
David sympathizes, and so he's taking this part of the book to start helping us look at tests from a design perspective. Ultimately, one hopes, we want to make automation that helps us do the mundane so we can focus on the much more interesting. We want to have tests that actually "test" for something, and have a clear idea if that test actually passed or failed. We want to be able to run these tests hundreds, thousands, maybe even millions of times, with a high surety that they will work the same way time one or time one million. To get there, we have to plan what we want to do, and start using some patterns to help us make tests that are effective and adaptive.
A side note, it was about this area of things, as we started to write "real code", that my project fell apart two years ago. This time we are doing this on a different OS, much more akin to the source of these examples, but still different enough that I wonder what will happen, so here we go. Fingers crossed :).
What We All Do At First
For most of us, making the move from Selenium IDE to Selenium RC (Version 1) or WebDriver (version 2) usually looks the same. We first use the IDE to get a handle on what we can do. We structure our tests to accomplish some goal, whatever that goal might be. It may just be to show we can click through the pages, as my example demonstrates here:
Once we've done that, we then export our test to a file format we want to use. For this book, and to be consistent with the examples shown, we will export to Java/JUnit4/Webdriver format.
From here, we can save the file somewhere, and when we open it, it will be Java/JUnit4/Webdriver format:
From here, we use IDEA (our Java IDE) to create a class in the test folder we set up, copy and paste the java code into an appropriately named file (in this case, JavaJunit4WebDriverFormat.java):
Once it's loaded, compile the java file (more keyboard ninjadom, click [Command]+[Shift+[fn]+F9). After that, you can click the debug test button (or [shift]+[fn]+F9) or the play test button (or [shift]+[fn]+F10).
Note: make sure that selenium-server is running in the background. I've been using "java -jar selenium-server-standalone-2.28.0.jar" and thus far, it's working as expected.
Look familiar? I'm willing to bet yes :). There are advantages to this approach. It's fairly easy to build up a battery of scripts this way. The disadvantage? Our battery of tests are going to be very linear, very literal and they are going to have a lot of duplication across several files. Need to change something? Get ready to change it multiple times. Ugh!
David's point with this chapter is that, with a little thought and some practical programming maxims, we can refactor our tests and design them, up front, so that the parts we use regularly will already be set up and usable, so that we can focus on the actual test creation process.
Initial Setup Details
Each file we create will use the following import statements,so either add them to your test template, or just have them close at hand to copy/paste:
import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy;
Using Page Object Patterns to Design Tests
I will admit, this is bending my brain a bit. I haven't actively coded in Java for, well, a decade, at least. thus, I'm finding the concepts to make sense, but getting the fingers to do the talking is taking longer than am used to. On the bright side, I am getting reacquainted with those long frozen synapses and dusting off that long ago accessed sector of my brain's hard drive (wouldn't it be neat if there was such a thing as a brain defrag?!). Anyway…
We're going to look at setting up three files that rely on each other and use the import commands we have listed earlier.
1. Create a new Java Class called HomePage.java.
2. Create another file, Chapter2.java
3. Create a third file called BestPractices3.java
Sorry, no copy and paste options here. If you want that capability, but the book ;).
Here, we are creating instance objects for the home page and the chapter, to which we can then pass the Selenium object to.
So what makes this interesting? Instead of having all of the functionality in one monolithic text file, we have split up the sections of the work into separate areas. We call on a method to create an instance of the browser, we call on a chapter class to perform the function of getting to the Chapter 2 page, and then we leave the discrete areas of the test in their own file. If we want to change some aspects of one file, we don't necessarily have to make changes to any of the other two. One interesting note. In the book, the call made in BestPractices3.java is assertTrue(ch2.isButtonPresent("but1"));
This looks to either be deprecated or no longer a method. assert(ch2.isButtonPresent("but1")); works, though.
OK, I have come to a point where the stuff on the printed page does not match what my compiler expects to see. I've tried looking at this from a number of different perspectives, and to se if I can anticipate the changes needed, but some of this goes beyond my current Java-Fu. Some of this may also be the state of the development of the modules for both JUnit and Selenium, and those changes aren't referenced here, or maybe we are looking at aspects of "implicitly understood things". Either way, I'm kinda stuck at the moment, so I'm going to ask for a little help :).
Where I'm At
Currently, I am looking at the ways that we can clean up the code to make it less dependent on multiple class, and have the declarations and object creation be simplified, so that areas that need to be changed can be changed in fewer places.
The first statement asks us to change the class for Chapter2.java to read as follows (annotations made to show where things didn't quite match):
For Part 2, we need to make a few changes:
- The How method is not recognized unless we pull in the actual How in the import section.
- We removed the line for the verifybutton in Chapter2 class.
- The isButton function is different, and asked us to change the boolean to bool (compiler doesn't like that, and to change the return value to look for findElementByXpath, which likewise doesn't exist.
These changes make for a not happy compiler:
But again, when compiled, lots of complaining.
As it stands, I cannot run the test, even though the book says I should be able to with these changes (both the ones before and what would make sense for this debug session).
Updated: See "Changes to Make PageFactory Work" in the next section. The examples and how they build up just will not work as described.
Changes to Make PageFactory Work
After playing around with it for a bit, and going forward and looking at later examples, I came to the conclusion that there are some edits that need to be made to these examples to make them actually work as described.
First, the includes are a little more numerous than described, though I think I've narrowed them down to what they need to be. Second, the PageFactory example should be defined in the HomePage class. The rest of the changes are visible below:
Now, with these modifications, if we run the test file, we successfully run and complete the test:
OK, so what have we accomplished here. Short of doing some moving around of methods, called and imports, the key thing we have accomplished is the reduction of code that was duplicated in multiple places. Now, any changes needed will allow for easier updates and tweaks for specific methods and modules, without having to gut everything nd chase down dependencies. That's cool.
Using LoadableComponent with PageObjects
We can sadd another base class to our scripts to allow us some additional functionality to our scripts. This class is called LoadComponet and consists of the following methods:
It's called as follows:
public class PageObject extends LoadableComponent < PageObject >
Sorry for the above, but blogger doesn't like having PageObject between brackets, The load method, as its name indicates, will load the page for us. The isLoaded() method is supposed to let us see if the page has loaded correctly.
Unfortunately, try as I might, I cannot make this version work, even with the errata options displayed at http://www.packtpub.com/support/10129. This is what I have:
And this is what I get when I run it (it does compile after I add the try catch code for the exception).
So again I get what it's trying to do, but alas, I am stymied in trying to get it to actually run and complete successfully.
Phew! That was a lot, and sadly, there was a lot that had to be re-interpreted and re-applied, because the code examples don';t line up with the compiler and reality. I was able to make two of the three explained examples work, and I will do some more digging to see if there's something I'm missing that can, I hope, get this to run, but for now, I will suffice it to say "interesting ideas, I can see how they are helpful to reduce code complexity and duplication, but it's really frustrating to not have the code actually work as displayed and to have to go in and make code changes to make the examples work. Another recommendation for Packt when it comes to demonstrating or showing code changes. As I have done here, I've shown what I can run and what I can't, perhaps in the book, it would be helpful to demonstrate as I have, finished code for all modules required, and to also show that the code was indeed proof-read and executed to confirm that it was working, and to also include what versions of code were used (the JUnit, selenium-server, java and Intelij IDEA versions, for instance).
End of Section.
Michael Larsen, TESTHEAD