To get this going we must...
Obtain the following
(version numbers are for my reference more than anything else; I'd always recommend getting the latest of everything)Eclipse - my ide of choice - (I've gone for Kepler Service Release 1, Build 20130919-0819)
Java SDK (1.6.0_23-b05)
JBehave - I downloaded the latest jbehave-web-3.5.5
Selenium Standalone Server (2.37.0)
Selenium-java (2.37.0)
Eclipse JBehave Plugin (1.0.0.20130304-2201) http://jbehave.org/reference/eclipse/updates/
FireFox (25.0)
Helpful stuff/FireFox add-ins: Firebug (1.12.4), Firefinder for Firebug (1.4), xPath Checker (0.4.4)
Step 1 - Create a Project in Eclipse
Should be simple.Step 2 - Configure Build Path
I have included all of the jars located in jbehave-web-3.5.5\lib in my build path library.I have included selenium in my build path library.
There might also be a selenium-firefox driver in the library also...perhaps if needed add in the standalone-server to get this jar, or search the web.
Step 3 - Create Assets
Three files are needed for this. The .story file which contains Given|When|Then BDD steps, a Step-Implementer which will do all the work, and a Test-Configuration which will manipulate Eclipse in to thinking we have a JUnit test sitting here, and that it knows how to run it.My understanding of the Test-Configuration is limited - I gather it has something to do with telling our old wolf, JBehave, a bit about how to dress up in lamb's clothing. Or something like that.
Time to get in to creating the example files. Please note the file names, google_hello_world.story, GoogleHelloWorld.java and GoogleSteps.java
The first two actually are closely related, everytime the .java contains a capital the .story contains an '_'. This is a simple implementation where the Test is simply trying to find a story with the 'same' name.
inside google_hello_world.story I place the text:
Scenario: Perform a search for the almighty Book of the Faces
Given I navigate to google.co.nz
When I perform a search for "facebook"
Then A link "Welcome to Facebook - Log In, Sign Up or Learn More" exists in the results
Given I navigate to google.co.nz
When I perform a search for "facebook"
Then A link "Welcome to Facebook - Log In, Sign Up or Learn More" exists in the results
inside GoogleHelloWorld.java I place the text:
package nz.co.whiteboxit.jbehaveselenium.demo;
import java.util.List;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.MostUsefulConfiguration;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.LoadFromClasspath;
import org.jbehave.core.io.StoryFinder;
import org.jbehave.core.junit.JUnitStory;
import org.jbehave.core.reporters.Format;
import org.jbehave.core.reporters.StoryReporterBuilder;
import org.jbehave.core.steps.CandidateSteps;
import org.jbehave.core.steps.InstanceStepsFactory;
public class GoogleHelloWorld extends JUnitStory{
@Override
public Configuration configuration(){
return new MostUsefulConfiguration().useStoryLoader(
new LoadFromClasspath(this.getClass()))
.useStoryReporterBuilder(
new StoryReporterBuilder().withCodeLocation(
CodeLocations.codeLocationFromClass(this
.getClass())).withFormats(
Format.CONSOLE, Format.TXT, Format.HTML, Format.STATS))
;
}
@Override
public List<CandidateSteps> candidateSteps(){
return new InstanceStepsFactory(configuration(),
new GoogleSteps()) //can put in a comma separated list of Step implementers here
.createCandidateSteps();
}
protected List<String> storyPaths(){
return new StoryFinder().findPaths(
CodeLocations.codeLocationFromClass(this.getClass()),
"*.story", "");
}
}
inside GoogleSteps.java I place:
package nz.co.whiteboxit.jbehaveselenium.demo;
import java.io.File;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.openqa.selenium.By;
import org.openqa.selenium.ElementNotVisibleException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxBinary;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
public class GoogleSteps {
private WebDriver driver;
private FluentWait<WebDriver> fWait;
public GoogleSteps() {
File pathToBinary = new File("[RELEVANT PATH]\\AppData\\Local\\Mozilla Firefox\\firefox.exe");
FirefoxBinary ffBinary = new FirefoxBinary(pathToBinary);
FirefoxProfile firefoxProfile = new FirefoxProfile();
FirefoxDriver driver2 = new FirefoxDriver(ffBinary, firefoxProfile);
// driver = new FirefoxDriver();
driver = driver2;
fWait = new FluentWait<WebDriver>(driver).pollingEvery(500,
TimeUnit.MILLISECONDS).withTimeout(10, TimeUnit.SECONDS);
}
@Given("I navigate to google.co.nz")
public void iNavigateToGoogleCoNz(){
driver.get("http://www.google.co.nz");
fWait.until(ExpectedConditions.visibilityOfElementLocated(By.id("gbqfq")));
}
@When("I perform a search for \"$query\"")
public void iPerformASearchForQuery(String query){
driver.findElement(By.id("gbqfq")).sendKeys(query);
}
@Then("A link containing \"$text\" exists in the results")
public void aLinkContainingTextExistsInTheResults(String resultText){
waitForElementToBePresent(By.partialLinkText(resultText), 5000);
}
@Then("A link \"$text\" exists in the results")
public void aLinkWithTextExistsInTheResults(String resultText){
waitForElementToBePresent(By.linkText(resultText), 5000);
}
public WebElement waitForElementToBePresent(By by, int waitInMilliSeconds)
{
int wait = waitInMilliSeconds;
int iterations = (wait/250);
long startmilliSec = System.currentTimeMillis();
for (int i = 0; i < iterations; i++)
{
if((System.currentTimeMillis()-startmilliSec)>wait)
return driver.findElement(by);
List<WebElement> elements = driver.findElements(by);
if (elements != null && elements.size() > 0)
return elements.get(0);
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return driver.findElement(by);
}
}
This should run as is within Eclipse as a JUnit test.
Future work/recommended improvements
My sync/wait/check mechanisms might need some improvement - I suspect there may be a better way.The run results are rubbish at present - only one JUnit looking 'test' for however many tests actually exist.
The story must have the same name as the JUnitRunner test - again, this is only because I'm lazy/low on time/aiming for the bare minimum.
This could be distributed/run using Selenium Server - I'll include this below since I've already implemented this in my demo.
Selenium bits could be way tidier; ideally abstract away from objects and implementations a little to get something with a bit more reuse.
Optional - Enhancing this be distributed/run using Selenium Server
Launch a Selenium Server Hub
java -jar "[RELEVANT PATH]\JBehaveArea\selenium-server-standalone-2.37.0.jar" -role hub -trustAllSSLCertificates
Launch a Selenium Server Nodejava -jar "[RELEVANT PATH]\JBehaveArea\selenium-server-standalone-2.37.0.jar" -role node -hub http://localhost:4444/grid/register -browser "browserName=firefox, version=25.0, firefox_binary=[RELEVANT PATH]\Local\Mozilla Firefox\firefox.exe, maxInstances=3, platform=WINDOWS"
Alter the Driver used in the code from
driver = new FirefoxDriver(ffBinary, firefoxProfile);
with some stuff that is going to look a bit like this:DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setBrowserName("firefox"); CommandExecutor executor = new SeleneseCommandExecutor(new URL("http://localhost:4444/"), new URL("http://www.google.co.nz/"), capabilities); WebDriver driver = new RemoteWebDriver(executor, capabilities);
Further Reading
I have discovered a superb Selenium tutorial at http://www.seleniumhq.org/docs/03_webdriver.jspOn the JBehave front I am a fan of http://christophertownson.com/
To use with IE:
ReplyDeleteSystem.setProperty("webdriver.ie.driver", ...\\IEDriverServer.exe");
driver = new InternetExplorerDriver();
And this causes objects to break. Most specifically the fluent wait needs to be modified to
fWait = new FluentWait(driver).withTimeout(15, TimeUnit.SECONDS)
.pollingEvery(2, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class)
and identification of the text box entry search field needs to be updated to
By.name("q")