Wednesday, 20 November 2013

Selenium and JBehave, a Hello World

This is intended to be the simplest JBehave hello world out there. And my implementation really is the bare minimum I can do and still sleep at night.

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





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 Node
java -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.jsp 
On the JBehave front I am a fan of http://christophertownson.com/tag/bdd


Apology

I'd like to add the disclaimer that I have not actually compiled and run the above as a whole; I have put together, compiled and run something extremely extremely similar, but for the above I have merely mentally compiled - a little gendankenexperimenting - and this works fine (in my head). So feedback welcome. I'll even go out of my way to fix any bugs people might have.

Saturday, 9 November 2013

JUnit Hello World in its simplest form

Here is a very simple, very short example of running JUnit.


Assumptions: using Eclipse
Get: JUnit jar - this might actually be in Eclipse, I've never paid enough attention to this.
You might need to associate JUnit to the project's build path.
The reason for vagueness is I control+1 all of my errors to quick fix them, and minimize my own learning at the same time.

So, what to  do:

Write a test class
e.g.
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class HelloJUnit{
@Before
public void myBefore(){
System.out.println("This is the before bit");
}
@After
public void myAfter(){
System.out.println("This is my after");
}
@Test
public void makePie(){
System.out.println("Make me some pie");
}
@Test
public void moarPies(){
System.out.println("Make me even moar pies");
}
}
Then in eclipse you go Run > Run Configurations... and choose a new JUnit test. Get it to run all, or whatever, and click go/run. 

Robot Framework, Basic Setup

Plug: Robot Framework is quick to setup, easy to write tests for, and super fast to triage failures in. The last point really sets it apart...