SQL Data Bindings for Gwen

SQL Data Bindings have been introduced to Gwen v2.1.0 to enable easy access to data stored in databases. This is handy for cases where you need to fetch data from a database and do some work with that data. For example, checking that the value of a field in a database contains some expected value or assigning a value stored in (or derived from) a database to an attribute and doing something with it.

The DSL for binding the result of an SQL query to an attribute is defined as follows:

<attribute> <is|will be> defined by sql "<selectStmt>" in the <dbName> database

Where:

  • attribute = the name of the attribute to bind the fetched data to
  • selectStmt = the SQL select statement used to fetch the data
  • dbName = a unique name assigned to the database connection

DB Connection Setup

Setting up a database connection is a two step once-off process.

  1. Configure the database connection by defining the following Gwen settings. Replace dbName  with the name you want to assign to your connection.
    • gwen.db.dbName.driver = the name of the JDBC driver class
    • gwen.db.dbName.url = the URL to the database containing all connection properties including credentials
  2. Point your GWEN_CLASSPATH environment variable to the location of the JDBC driver JAR. You could choose to set this in a wrapper script (bat or shell) or in the environment settings of your local machine.

For more details, see SQL Data Bindings on our wiki.

Any number of database connections can be configured. You just need to assign a unique dbName to each connection and configure the driver and url settings using that name and add any required JDBC driver JARs to your GWEN_CLASSPATH variable (multiple JAR entires can be delimited using the standard Java classpath separator for the platform you are on).

Simple Usage

Assuming you have a database named ‘feedback‘ configured as per the MySQL example here, you can then bind an SQL query that fetches an email address (for example) to an attributed named ‘my email‘ as shown below. Ideally you should define this binding in a Gwen Meta file.

Given my email is defined by sql "select email from subscribers where name='gweninterpreter'" in the feedback database

In this example:

  • attribute = my email
  • selectStmt = select email from subscribers where name=’gweninterpreter’
  • dbName = feedback

With this binding in place, you can then reference the ‘my email‘ attribute anywhere in your features and Gwen will execute the SQL query to fetch the email value from the database and bind it in place.

For example, if you perform an assertion as follows, Gwen will fetch and bind the ‘my email‘ value before doing the comparison.

# compare email in database with expected literal
Then my email should be "gweninterpreter@gmail.com"

As another example, you can also enter the fetched email value into a field on a web page:

# web page field binding (in Meta)
Given the email field can be located by name "email"

# enter email value fetched from database into field on web page
When I enter my email in the email field

Advanced Usage

You can also use string interpolation to soft code the ‘name‘ parameter in the above query to another attribute called ‘my name‘ by redefining the SQL in your binding as follows instead:

select email from subscribers where name='${my name}'

Now whatever value is bound to the ‘my name‘ attribute in memory will be substituted for the ‘${my name}‘ placeholder in the SQL string before Gwen executes it. This value could be the captured value of a field on a web page, a property value, an assigned literal, or any other type of Gwen binding (another SQL binding even).

Advertisements

Gwen 2 released

Gwen 2 has been released with the latest technologies including Java 8 and Selenium WebDriver 3. All binary dependencies have also been updated and the project is now built using Scala 2.12.

There is one impact to Firefox users but nothing else has changed. With the upgrade to Selenium 3, all browser vendors (including Mozilla) are responsible for developing and providing the native drivers for their own browsers. To use Firefox, you will now need to download a native Firefox driver and set the following in your gwen.properties file:

webdriver.gecko.driver=/your-downloaded-driver-location/geckodriver

The settings page on our Gwen wiki has also been updated.

Gwen 2.x will only run on Java 8 environments. Gwen 1.x will still run on Java 7 and above as before, but will no longer have new features added to it.

The latest Gwen release is available here. See also install instructions.

Integrating Gwen with Maven

Although you can download and install Gwen locally to run automated tests, it is sometimes useful to integrate it with a build tool such as Maven and have it download and install it for you and run tests as part of the release verification process. Integrating with Maven can also make it easier for you to run Gwen on a continuous integration build server too.

Gwen does not ship with a Maven plugin, but you can still integrate it with Maven using a POM file like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <groupId>org.gweninterpreter</groupId>
  <artifactId>gwen-web-maven</artifactId>
  <name>gwen-web-maven</name>
  <version>4.0.0</version>
  <packaging>pom</packaging>

  <profiles>
    <profile>
      <id>gwen</id>
      <activation>
        <property>
          <name>gwen.args</name>
        </property>
      </activation>
      <properties>
        <gwen.web.version>???</gwen.web.version>
        <!--selenium.version>???</selenium.version-->
        <!--selenium.dir>target/selenium-${selenium.version}</selenium.dir-->
        <gwen.dir>target/gwen-web-${gwen.web.version}</gwen.dir>
        <gwen.classpath>${selenium.dir}/*${path.separator}${gwen.dir}/lib/*</gwen.classpath>
      </properties>
      <dependencies>
        <!--dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-java</artifactId>
          <version>${selenium.version}</version>
        </dependency-->
        <dependency>
          <groupId>org.gweninterpreter</groupId>
          <artifactId>gwen-web</artifactId>
          <version>${gwen.web.version}</version>
          <type>zip</type>
          <scope>provided</scope>
        </dependency>
      </dependencies>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.9</version>
            <executions>
              <execution>
                <id>install-gwen</id>
                <phase>integration-test</phase>
                <goals>
                  <goal>unpack-dependencies</goal>
                </goals>
                <configuration>
                  <includeGroupIds>org.gweninterpreter</includeGroupIds>
                  <includeArtifactIds>gwen-web</includeArtifactIds>
                  <includeTypes>zip</includeTypes>
                  <outputDirectory>target</outputDirectory>
                  <stripClassifier>true</stripClassifier>
                  <stripVersion>true</stripVersion>
                </configuration>
              </execution>
              <!--execution>
                <id>install-selenium</id>
                <phase>integration-test</phase>
                <goals>
                  <goal>copy-dependencies</goal>
                </goals>
                <configuration>
                  <excludeScope>provided</excludeScope>
                  <outputDirectory>${selenium.dir}</outputDirectory>
                </configuration>
              </execution-->
            </executions>
          </plugin>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.3.2</version>
            <executions>
              <execution>
                <id>launch-gwen</id>
                <phase>verify</phase>
                <goals>
                  <goal>exec</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <executable>java</executable>
              <commandlineArgs>-cp ${gwen.classpath} gwen.web.WebInterpreter -b ${gwen.args}</commandlineArgs>
              <successCodes>
                <successCode>0</successCode>
              </successCodes>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
  
</project>

Be sure to change the Gwen version by updating the gwen.web.version property at line 20 with the latest version or another version you would like to use.

With this POM and your Gwen settings in place you can have Maven download, install, and launch the latest version of Gwen with the following command (where <args> is a space separated list of arguments that you want to pass to Gwen):

mvn verify -Dgwen.args="<args>"

So if your executable tests are in a folder named ‘features’ relative to your POM file, then you can execute them with a Maven command like this:

mvn verify -Dgwen.args="features"

And to also generate html and junit style reports in the target/reports folder..

mvn verify -Dgwen.args="-r target/reports -f html,junit features"

Similarly, any arguments can be passed to Gwen in this manner through the gwen.args -D command line argument. Note that the -b batch mode switch is implicitly passed to Gwen and therefore you do not need to explicitly specify it as an argument. The gwen profile will only be activated if gwen.args is specified in the call to Maven.

By default, Gwen will use the latest version of Selenium bundled in its distribution but you can change it if you need to by performing the following. You would normally not need to do this unless you are targeting a legacy or beta browser or need to run a specific version of selenium.

  • Uncomment lines 21, 22, 27 to 31, and 61 to 71.
  • Update the selenium.version property (at line 21) with the release number you would like to use

No Page Objects – There’s no long way to go. We’re already there!

Specifications driven web automation with no page objects or selenium coding ~ We’ve done it!

Page Objects

I recently discovered a Page Objects Refactored article which suggests that ‘flaky’ web tests are often misattributed to Selenium web driver issues and that the problem instead is poorly coded page objects that were not written with solid OO principles in mind. Programming languages allow programmers to achieve the same or similar results in more than one way. For this reason, design patterns were introduced to help developers implement known solutions to known problems. But even design patterns require discipline and cannot be strictly enforced to any useful degree without introducing a specialised framework. Frameworks also are geared at software developers and not software users.

What is needed is a pre-programmed automation tool that frees users from development concerns.

No Page Objects

One of the primary goals of the gwen-web project is to give users a tool for automating web pages without needing to develop any page objects or compile any code. Gwen-web is an interpreter that accepts plain text Gherkin features as input and produces executing web driver instructions as output. All interactions with the web driver API happen in a precompiled and embedded web engine through a prescribed DSL. This web engine also handles many of the known gotchas and pitfalls that programmers typically face when working directly with the web driver. Users also have the flexibility to compose custom DSLs (their own step definitions) which are also expressed in Gherkin. With this approach, developing page objects and adopting design patterns is no longer required. Furthermore, programmer errors are eliminated and ‘flakiness’ is removed. The ability to compose specifications in plain Gherkin text is all that is necessary.

An example

Consider the following Gherkin feature:

 Feature: Google search

Scenario: Perform a google search
    Given I do a google search for "gwen-web"
     When I click the first result
     Then the page title should contain "gwen-interpreter/gwen-web"
      And the current URL should be "https://github.com/gwen-interpreter/gwen-web"

With Gwen, we can take a plain text feature like this one and execute it “as is”.

If you install gwen-web, you will find the above feature file located at features\google\GoogleSearch.feature relative to your root install directory (the location where you unpacked the distribution). If you open a command prompt to this location, you can execute this feature as follows:

On Windows:

gwen -b features\google\GoogleSearch.feature

On a Mac:

./gwen -b features/google/GoogleSearch.feature

When this is launched, gwen-web will:

  • Open a new browser session
  • Navigate to the google home page
  • Submit a Google search
  • Click the first result that comes back
  • Verify that the title of the resulting page contains some expected text
  • Check the URL of the resulting page

How does it work?

Alongside the GoogleSearch.feature file, you will also find a Google.meta file. This file is also a Gherkin feature file, but with a .meta file extension. It defines the step definition for the first step in our feature. The remaining steps are all predefined in the web engine. We don’t need to define step definitions for those since the web engine already knows how to execute them.

Here are the contents of the Google.meta file (minus the comments):

 Feature: Google search

@StepDef
Scenario: I do a google search for "<query>"
    Given I navigate to "http://www.google.com"
      And the search field can be located by name "q"
     When I enter "$<query>" in the search field
     Then the page title should start with "$<query>"
      And the first match can be located by class name "r"
      And the first result can be located by tag name "a" in the first match

This meta defines an annotated scenario named: I do a google search for "<query>". This is how you define a step definition with Gwen. It’s simply just a @StepDef annotated scenario that in this case accepts a <query> string as a parameter followed by a sequence of steps that define its operations. When this meta file is processed by Gwen, the step definition is loaded and made available to any executing feature that is passed in. In our example, Gwen automatically discovers this meta file (because it is in the same directory as the feature itself) and preloads the step definition defined in it to memory. When the first step in our feature executes, Gwen matches it to the step definition by name and binds the passed in “gwen-web” string literal to the <query> parameter which is then used by the steps therein to perform the Google search.

Comparing the feature step with the step definition reveals the match that Gwen is able to detect:

I do a google search for "gwen-web"
I do a google search for "<query>"

Another example

You could also express this Serenity example as a feature specification like this:

features\todo\CompleteATodo.feature

   Feature: Complete a Todo

Background: Open a new browser
      Given I start a browser for James

  Scenario: I should be able to complete a todo
      Given I browse to the application home page
       When I add a "Walk the dog" item
        And I add a "Put out the garbage" item
        And I complete the "Walk the dog" item
       Then the "Walk the dog" item should be completed

  Scenario: I should see the number of todos decrease when an item is completed
      Given I browse to the application home page
       When I add a "Walk the dog" item
        And I add a "Put out the garbage" item
        And I complete the "Put out the garbage" item
       Then the number of items left should be "1"

And make it executable with a meta specification like this:

features\todo\Todo.meta

 Feature: Todo meta

@StepDef
Scenario: I browse to the application home page
    Given I navigate to "http://todomvc.com/examples/angularjs/#/"
     Then the todo field can be located by id "new-todo"
      And the number of items left can be located by javascript "document.getElementById('todo-count').children[0]"
      And I locate the todo field

@StepDef
Scenario: I add a "<todo>" item
    Given I enter "$<todo>" in the todo field
      And the "$<todo>" item can be located by xpath "//*[@class='view' and contains(.,'$<todo>')]//label"
      And the "$<todo>" item checkbox can be located by xpath "//*[@class='view' and contains(.,'$<todo>')]//input[@type='checkbox']"

@StepDef
Scenario: I complete the "<todo>" item
    Given I click the "$<todo>" item checkbox

@StepDef
Scenario: the "<todo>" item should be completed
    Given the "$<todo>" item checkbox should be checked

And then launch it like this (assuming you have created the above two files):

Windows:

gwen -b features\todo\CompleteATodo.feature

Mac:

./gwen -b features/todo/CompleteATodo.feature

Composable automation

With Gwen we don’t have to do any heavy development work. We only need to compose specifications.

Notice that all the technical automation glue (JavaScript expressions, XPath locators etc..) are confined to meta. The features themselves are clean, readable, self documenting, and easily understood. You could easily have a BA or tester write the feature files upfront and then have a developer plug in some meta behind it. Or you could have tech savvy testers with enough HTML and JavaScript skills to write the meta themselves. Teams can choose to be as declarative or as imperative as they like in how they write their features. Tailored meta can always be crafted to suit.

So there you have it ~ web automation with #NoPageObjects and no selenium coding!

If you would like to see a more detailed example, then see our Automation by Meta blog post. See also our Wiki and FAQ pages for more information about Gwen.

Configurable User Properties in gwen-web

One of the really cool features of gwen-web is the ability to load in different parameters at run time.  This allows you to run gwen-web against multiple environments without changing either the feature or the meta files.

Lets take one of the examples with gwen-web and flood.io   Below are two examples of how one could set the value.  The first example sets “the how old are you dropdown” to 21, the second example uses a property, in this case different files for two different environments to set the value.

Lets define how the dropdown can be found in the meta first.

FloodIO.meta

And the heading should be “Step 2”
And the how old are you dropdown can be located by id “challenger_age”

FloodIO.feature

When I select “21” in the how old are you dropdown

Now with the properties approach. Lets create two files, DEV.properties and SIT.properties. The contents of the two files are below:

DEV.properties
user.age=44

SIT.properties
user.age=21

In addition we need to make a small modification to both the feature and the meta files

FloodIO.meta

And the heading should be “Step 2”
And my age is defined by property “user.age”
And the how old are you dropdown can be located by id “challenger_age”

Now replace the strikethrough line with the new line in the feature

FloodIO.feature

When I select “21” in the how old are you dropdown
When I select my age in the how old are you dropdown

Editting is complete. To run gwen-web with the environment specific age, simply type “gwen-web features/floodio/ -p dev.properties -b -r devReport”, or alternatively you can run “gwen-web features/floodio/ -p sit.properties -b -r sitReport”.

Remote WebDriver feature now available in gwen-web

For my first post, I will be talking about a new feature that has been added to gwen-web.  For those of you not familiar with gwen-web, its a web automation engine that runs inside our gwen interpreter and is used to automate the browser.  gwen-web is unique in that it allows automation of the web browser, using english(dsl) rather than coding or using visual record/playback. We believe this will remove some of the entry level requirements often associated with a coded tool.  For more information please head on over to our gwen github page https://github.com/gwen-interpreter/gwen.

Remote WebDriver

Just recently we added the remote webdriver capability.  This means gwen-web can now connect to selenium grid and drive not just the local browser, but also a browser that is installed on a different os, or a browser that has a different version.

Setting up Remote WebDriver with gwen-web

So how do I setup gwen-web with selenium remote webdriver?  What I am going to walk you through is setting up a selenium grid, and attaching nodes to it.  Firstly download the selenium-server-standalone-2.45.0.jar (or most recent) from http://selenium-release.storage.googleapis.com/2.45/selenium-server-standalone-2.45.0.jar.   Once this has been downloaded, you can start up selenium grid by running the following:

java -jar selenium-server-standalone-2.45.0.jar -role hub

Now lets make sure that its running by navigating to http://localhost:4444/grid/console.  You will notice that there are currently no browsers available.

PastedGraphic-1

Running selenium grid in this mode, means we need to register nodes against the grid.

Adding browsers to the current running grid

Lets start by downloading the latest version of chromedriver (specific for the OS) – from http://chromedriver.storage.googleapis.com/index.html?path=2.15/  (at the time of writing the blog chromedriver 2.15 is the latest)

Unzip chromedriver and note the path.

Now lets start up selenium-server-standalone as a node role, and register both the chrome driver and firefox driver.  Remembering firefox driver comes with the selenium-server-standalone.

java -jar selenium-server-standalone-2.45.0.jar 
-Dwebdriver.chrome.driver=chromedriver_215/chromedriver 
-role node 
-hub http://localhost:4444/grid/register 
-browser browserName=firefox,version=37.0,maxInstances=5,platform=LINUX 
-browser browserName=chrome,version=42.0,maxInstances=5,platform=LINUX

Let me explain the parts above:

  1. java -jar selenium-server-standalone-2.45.0.jar  (standard selenium startup)
  2. -Dwebdriver.chrome.driver= (path to chromedriver including the executable)
  3. -role node (run the selenium-server-standalone as a node)
  4. -hub http://localhost:4444/grid/register  (this is the location of the grid that you started back under the previous heading “Setting up  Remote WebDriver with gwen-web”
  5. -browser browserName=firefox,version=37.0,maxInstance=5,platform=LINUX  (the browser, version label and the number of maximum instances to run on this node)  Note:  firefox requires the binary to be in the PATH, alternatively it would need to be specified using -Dwebdriver.firefox.bin
  6. -browser browserName=chrome,version=42.0,maxInstances=5,platform=LINUX.  (register that this node is capable of running chrome)

Testing the grid console

So how do we now confirm the currently registered browsers on the grid.   Navigate to http://localhost:4444/grid/console and now the console should show both firefox and chrome registered and waiting for a connection.

PastedGraphic-2

When I setup a grid, I typically like to confirm whether or not the browsers do indeed start up.  To do this I navigate to the node webdriver/hub   E.g.  http://10.1.1.9:5555/wd/hub   (you can see the IP address of each node that is registered against this grid).

PastedGraphic-3

Then I create two sessions.  One for chrome and one for firefox.  If its working, both chrome and firefox will start.  Just delete the sessions once that has been confirmed.  If its not working, then you will need to go back and troubleshoot.  Typically its driver / binary path related.  If either browser pops up and then closes unexpectedly, make sure to keep both the selenium-server-standalone and the browser versions in sync.  The latest version of selenium-standalone may not work with a version of the browser that is 6 versions out of date.

Setting up gwen-web to work with selenium-grid

Once you have a working grid, gwen web only requires a small change to the gwen.properties file. (eg. edit ~/gwen.properties and add the following line.)

gwen.web.remote.url=http://localhost:4444/wd/hub

This tells the gwen-web engine that it needs to communicate with selenium grid which will effectively use the RemoteWebDriver with different browser capabilities.

Running gwen-web with selenium-grid

To run gwen-web with two different browsers it is suggested that you remove gwen.web.browser property from the gwen.properties and pass it in as a “-D” system property.  The below example uses the floodio challenge sample with gwen-web.

eg.

bin/gwen-web -Dgwen.web.browser=chrome -b features/floodio -r reports/chrome
bin/gwen-web -Dgwen.web.browser=firefox -b features/floodio -r reports/firefox

Once gwen-web is running, navigate to the selenium node and you will see something like this

Screen Shot 2015-04-30 at 9.42.28 pm

and navigate to the grid localhost:4444/grid/console and you will now see both firefox and chrome running.

Screen Shot 2015-04-30 at 9.42.47 pm