Selenium being driven from JUnit is my preferred mode of operation.
I've made a movie of Selenium testing the PetSoar app under JUnit
control. The
other mode "standalone"
or "in-browser", is the original mode of
operation. It has to be said that the driven mode is a little slower,
but I prefer it greatly as I think it overcomes some glass ceilings I
perceive with the other.
The
movie was shot on the Mac in Firefox, but don't forget that
Selenium is designed for Web Browsers anywhere. It
is 10Mb, and requires quicktime.
It is from the next version of Selenium, which should be 0.4, testing
the
'PetSoar' app from
Joe Walnes'
(and friends)
Java
Open Source Programming book.
download here
The movie is only a few minutes long, starts with a screen shot of the
test source, switches to a shell to see Ant:
- Kick off a build (compile,
make webapp etc)
- Launch the webapp in its
container (Jetty)
- Launch or switch to Firefox
(look for the gratuitous advert for ThoughtWorks - our page for
hiring open
source developers)
- Starts the
Selenium tests.
Last of all, Ant is
returned to the front to see the JUnit results in the command log.
Imagine the usual JUnit reports.
During the tests the PetStore app is instantiated and torn down several
times. Once for each test method, in fact, as is usual for JUnit tests.
If the test were reworked to use a OneTimeSetup the same web
server instance could be used, but that might
introduce side effects into the testing. The blankish testing
screen that appears after testsComplete() and the delay before the
first PetStore page appears, because JSP pages are compiling again
after the web server restarts. That happens five times
during the test as there are, shock horror, five test methods.
The TestCase
package com.paulhammant.petstore;
import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;
import
com.thoughtworks.selenium.embedded.jetty.DirectoryStaticContentHandler;
import com.thoughtworks.selenium.embedded.jetty.JettyCommandProcessor;
import com.thoughtworks.selenium.launchers.SystemDefaultBrowserLauncher;
import junit.framework.TestCase;
import java.io.File;
public class FooTestCase extends TestCase {
Selenium selenium;
protected void setUp() throws Exception {
super.setUp();
File codeRoot = getCodeRoot();
selenium = new DefaultSelenium(
new
JettyCommandProcessor(new File(codeRoot, "war-for-selenium"),
DefaultSelenium.DEFAULT_SELENIUM_CONTEXT,
new
DirectoryStaticContentHandler(new File(codeRoot, "selenium"))),
new
SystemDefaultBrowserLauncher()
);
selenium.start();
}
private File getCodeRoot() throws Exception {
File codeRoot;
String codeRootProperty =
System.getProperty("code_root");
if (codeRootProperty == null) {
throw new
Exception("'code_root' not specified");
} else {
codeRoot = new
File(codeRootProperty);
if
(!codeRoot.exists()) {
throw new Exception("'code_root' not a dir");
}
}
return codeRoot;
}
protected void tearDown() throws Exception {
selenium.testComplete();
Thread.sleep(2 * 1000);
selenium.stop();
}
public void testLogin() {
selenium.setContext("Log into
PetStore");
goToFrontPage();
attemptLogin("duke","duke");
selenium.verifyTextPresent("Welcome: duke");
}
private void attemptLogin(String user, String
passwd) {
selenium.open("/login.jsp");
selenium.type("username", user);
selenium.type("password", passwd);
selenium.clickAndWait("Login");
}
public void testBogusLogin() {
selenium.setContext("Log into
PetStore");
goToFrontPage();
attemptLogin("lord","lady");
selenium.verifyTextPresent("Invalid username or password");
goToFrontPage();
selenium.clickAndWait("inventory");
selenium.verifyLocation("/login.jsp:");
}
public void testSearch() {
selenium.setContext("Test of
Searching For Pets");
selenium.open("/storefront/listpets.action");
selenium.verifyTextPresent("Pets
In Stock");
selenium.type("query", "Cat");
selenium.clickAndWait("Submit");
selenium.verifyTextPresent("Tiger");
}
public void testInventory() {
selenium.setContext("Test to
check pet inventory");
attemptLogin("duke","duke");
goToFrontPage();
selenium.clickAndWait("inventory");
selenium.clickAndWait("ListPets");
selenium.verifyTextPresent("Lizard");
selenium.verifyTextPresent("Snake");
selenium.verifyTextPresent("Pigeon");
selenium.verifyTextPresent("Seagul");
selenium.verifyTextPresent("Rexio");
selenium.clickAndWait("PetStore");
}
private void goToFrontPage() {
selenium.open("/");
selenium.verifyTextPresent("Welcome to PetStore");
}
public void testButAPet() {
selenium.setContext("Test to buy
a pet from the inventory");
attemptLogin("duke","duke");
goToFrontPage();
selenium.clickAndWait("Pets");
selenium.verifyTextPresent("Lizard");
selenium.clickAndWait("pet-Lizard");
selenium.clickAndWait("addToCart");
selenium.verifyTextPresent("Pet
Added to Cart");
selenium.clickAndWait("checkOut");
selenium.verifyTextPresent("Credit Card Information");
selenium.clickAndWait("viewCart");
selenium.verifyTextPresent("$13.75");
selenium.clickAndWait("del-Lizard");
selenium.verifyTextPresent("Pet
Removed from Cart");
selenium.verifyTextPresent("This
Phrase Is Not In The Page");
selenium.clickAndWait("Logout");
selenium.clickAndWait("PetStore");
}
}
With a procedural programming language, of course, you can use loops
and conditionals. All that is demonstrated above is some reusable
methods - goToFrontPage() and attemptLogin().
Next, I am going to try doing some
pull
the rug from under the feet of the app tests. e.g. Delete
Lizard from the DB after putting a lizard in a cart and before going to
checkout. Joe, Mike Cannon-Brookes, Ara and Pat Lightbody
probably did not test that explicitly, but their are such good coders I
think the PetSoar/Petstore app will gracefully indicate outage.
Also, in the next version I'm going to show something that is thought
to make Selenium testing more accessible to non-coders (potentially
many in the testing community). The thing in question would be Groovy
test scripts.
Thus ..
public void testLogin() {
selenium.setContext("Log into
PetStore");
goToFrontPage();
attemptLogin("duke","duke");
selenium.verifyTextPresent("Welcome: duke", "");
}
.. would become ..
void testLogin() {
setContext("Log into PetStore")
goToFrontPage()
attemptLogin("duke","duke")
verifyTextPresent("Welcome: duke")
}
May 7, 2005.