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 Browsersanywhere. 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:

  1. Kick off a build (compile, make webapp etc)
  2. Launch the webapp in its container (Jetty)
  3. Launch or switch to Firefox (look for the gratuitous advert for ThoughtWorks - ourpage for hiring open source developers)
  4. Starts the Seleniumtests.

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 usea OneTimeSetup the same web server instancecould 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 afterthe 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 willgracefully 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 ..

def testLogin() {
  setContext("Log into PetStore")
  goToFrontPage()
  attemptLogin("duke","duke")
  verifyTextPresent("Welcome: duke")
}


Published

May 7th, 2005
Reads:

Tags

Categories