Skip to end of metadata
Go to start of metadata

Selenium Bootcamp is a series of articles prepared by Selenium guru Dave Haeffner for Sauce Labs. Dave also authors the Elemental Selenium website, which includes tips for using Selenium, and where you can sign up for his weekly email on the topic of Selenium testing. 


Now that we have some tests and page objects, we'll want to start thinking about how to structure our test code to be more flexible. That way it can scale to meet our needs.

Global Setup & Teardown

We'll start by pulling the Selenium setup and teardown out of our tests and into a central location.

To do that, we'll want to create a base test. So let's create a new file called Base.java in the testspackage.

├── pom.xml
├── src
│      └── test
│              └── java
│                      ├── pageobjects
│                      │      ├── DynamicLoading.java
│                      │      └── Login.java
│                      └── tests
│                              ├── Base.java
│                              ├── TestDynamicLoading.java
│                              └── TestLogin.java

And here are the contents of the file.

// filename: tests/Base.java
package tests;
import org.junit.Rule; import org.junit.rules.ExternalResource; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver;
public class Base {
protected WebDriver driver;

@Rule
public ExternalResource resource = new ExternalResource() {

    @Override
    protected void before() throws Throwable {
        driver = new FirefoxDriver();
    }

    @Override
    protected void after() {
        driver.quit();
    }

};

}

After importing a few necessary classes we specify the Base class and wire up some methods that will take care of setting up and tearing down Selenium before and after every test.

It's worth noting that we could have used methods with @Before and @After annotations just like before. But if we did that we'd be giving up the ability to use these annotations in our tests (which we'll want to use for page object instantiation).

To preserve this functionality we're using JUnit's ExternalResource Rule. This rule has before and after methods that execute prior to methods annotated with @Before and @After. For more info on JUnit's Rules, read this. Now let's update our tests to establish inheritance with this base test class, remove the Selenium setup/teardown actions, and remove the unnecessary import statements. When we're done our test files should look like this:

// filename: tests/TestLogin.java package tests;
import org.junit.Test; import org.junit.Before; import static org.junit.Assert.*; import pageobjects.Login;
public class TestLogin extends Base {
private Login login;

@Before
public void setUp() {
    login = new Login(driver);
}

@Test
public void succeeded() {
    login.with("tomsmith", "SuperSecretPassword!");
    assertTrue("success message not present",
            login.successMessagePresent());
}

@Test
public void failed() {
    login.with("tomsmith", "bad password");
    assertTrue("failure message wasn't present after providing bogus credentials",
            login.failureMessagePresent());
    assertFalse("success message was present after providing bogus credentials",
            login.successMessagePresent());
}

}
// filename: tests/TestDynamicLoading.java
package tests;
import org.junit.Test; import org.junit.Before; import static org.junit.Assert.*; import pageobjects.DynamicLoading;
public class TestDynamicLoading extends Base {
private DynamicLoading dynamicLoading;

@Before
public void setUp() {
    dynamicLoading = new DynamicLoading(driver);
}

@Test
public void hiddenElementLoads() {
    dynamicLoading.loadExample("1");
    assertTrue("finish text didn't display after loading",
            dynamicLoading.finishTextPresent());
}

@Test
public void elementAppears() {
    dynamicLoading.loadExample("2");
    assertTrue("finish text didn't render after loading",
            dynamicLoading.finishTextPresent());
}

}

Running Tests On Any Browser

If you've ever needed to test features in an older browser like Internet Explorer 8 then odds are you ran a virtual machine (VM) on your computer with a "legit" version of Windows XP.

Handy, but what happens when you need to check things on multiple versions of IE? Now you're looking at multiple VMs. And what about when you need to scale and cover other browser and Operating System (OS) combinations? Now you're looking at provisioning, running, and maintaining your own set of machines and standing up something like Selenium Grid to coordinate tests across them.

Rather than take on the overhead of a test infrastructure you can easily outsource this to a third-party cloud provider like Sauce Labs.

An Example

With Sauce Labs we need to provide specifics about what we want in our test environment, our credentials, and configure Selenium a little bit differently than we have been. Let's start by creating a file for these various configuration values.

Let's create a file called Config.java in the tests package.

├── pom.xml
├── src
│   └── test
│       └── java
│           ├── pageobjects
│           │   ├── DynamicLoading.java
│           │   └── Login.java
│           └── tests
│               ├── Base.java
│               ├── Config.java
│               ├── TestDynamicLoading.java
│               └── TestLogin.java

In it we'll create an interface and specify field variables for the different configuration values we need.

 // filename: tests/Config.java


package tests;

public interface Config { 
	final String baseUrl = System.getProperty("baseUrl", "http://the-internet.herokuapp.com"); 
	final String browser = System.getProperty("browser", "firefox"); 
	final String host = System.getProperty("host", "localhost"); 
	final String browserVersion = System.getProperty("browserVersion", "33"); 
	final String platform = System.getProperty("platform", "Windows XP"); 
	final String sauceUser = "your-sauce-username"; 
	final String sauceKey = "your-sauce-access-key"; }

host enables us to specify whether our tests run locally or on Sauce Labs. If we don't specify a value at runtime, then the tests will execute locally.

With browserbrowserVeresion, and platform we can specify which browser and operating system combination we want our tests to run on. You can see a full list of Sauce's available platform options here. They also have a handy configuration generator (which will tell you what values to plug into your test) here. We've made so if no values are provided at run time, they will default to firefoxversion 33 running on Windows XPsauceUser is your Sauce username, and sauceKey is your Sauce Access Key (which can be found on your account dashboard). An alternative to hard-coding your credentials is to store them in environment variables and retrieve them.

java final String sauceUser = System.getenv("SAUCE_USERNAME"); final String sauceKey = System.getenv("SAUCE_ACCESS_KEY");

Do whichever you're more comfortable and familiar with.

Now we can update our base test class to work with Selenium Remote (which is how we'll be able to connect to Sauce Labs).

// filename: tests/Base.java 
... 
	@Override 
	protected void before() throws Throwable {
 		if (host.equals("saucelabs")) { 
			DesiredCapabilities capabilities = new DesiredCapabilities(); 
			capabilities.setCapability("browserName", browser); 
			capabilities.setCapability("version", browserVersion); 
			capabilities.setCapability("platform", platform); String sauceUrl = String.format("http://%s:%s@ondemand.saucelabs.com:80/wd/hub", sauceUser, sauceKey); 
			driver = new RemoteWebDriver(new URL(sauceUrl), capabilities); 
		} else if (host.equals("localhost")) {
			if (browser.equals("firefox")) {
			 driver = new FirefoxDriver();
			} else if (browser.equals("chrome")) {
				System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/vendor/chromedriver"); 
				driver = new ChromeDriver(); 
			} 
		} 
	} 
... 

In our before method we've added a new conditional flow (e.g., if/else if) to check the hostvariable.

We start by checking to see if it's set to "saucelabs". If it is we create a DesiredCapabilitiesobject, populate it (with browserbrowserVersion, and platform values), and connect to Sauce Labs using Selenium Remote (passing in the DesiredCapabilities object). This will return a Selenium WebDriver object that we can use just like when running our tests locally.

If the host variable is set to "localhost" then our tests will run locally just like before.

If we save everything and run our tests in Sauce Labs (e.g., mvn clean test -Dhost=saucelabs) then on the account dashboard we'll see our tests running in Firefox 33 on Windows XP.

And if we wanted to run our tests on different browser and operating system combinations, here are what some of the commands would look like:

mvn clean test -Dhost=saucelabs -Dbrowser="internet explorer" -DbrowserVersion=8
mvn clean test -Dhost=saucelabs -Dbrowser="internet explorer" -DbrowserVersion=10 -Dplatform="Windows 8.1"
mvn clean test -Dhost=saucelabs -Dbrowser=firefox -DbrowserVersion=26 -Dplatform="Windows 7"
mvn clean test -Dhost=saucelabs -Dbrowser=safari -DbrowserVersion=8 -Dplatform="OS X 10.10"
mvn clean test -Dhost=saucelabs -Dbrowser=chrome -DbrowserVersion=40 -Dplatform="OS X 10.8"

Notice the properties with quotations (e.g., "internet explorer" and "OS X 10.10"). When dealing with more than one word in a runtime property we need to make sure to surround them in double-quotes (or else our test code won't compile).


That wraps up The Selenium Bootcamp.

There's a whole lot more to think about when it comes to using Selenium successfully. If you’re interested in learning what it takes and how to do it, then grab a copy of The Selenium Guidebook today!

  • No labels