BDD with Squish and Behave

Last edited on

Introduction

Behaviour Driven Development has gained in popularity in recent years. Behaviour Driven Testing allows you to create acceptance tests for your application in natural language, making it simple to use even for non-technical team members. Test cases are usually created using Gherkin syntax, which makes them easy to read. Once created, test cases can be automated using Squish.

Behave is a BDD tool which reads a Gherkin style file and applies Python code to execute it. This article demonstrates how easy it is to integrate Behave and Squish. In similar ways Squish can be integrated with other Python-based BDD tools like Lettuce or Freshen.

As an example application under test Java application AddressBook is chosen, which is shipped together with Squish. Using a similar approach, we can automate Qt, Windows, Mac, iOS, Android, Web applications.

Feature definition

Let us start by defining how our Feature “Filling of address book” shall behave. To do that we create a so-called feature file using the Gherkin syntax. To make it more interesting to some steps parameters were added (like “Then “0” entries should be present”, “Then “2” entries should be present”) and some steps are data-driven (like “When adding persons to address book”)

addressbook.feature

Feature: Filling of address book

Scenario: Initial state of created address book

Given no prior existing address book

When I create a new addressbook

Then “0” entries should be present

Scenario: State after adding two entries

Given a newly created addressbook

When adding persons to addressbook

 | firstname | lastname  | email     | phone     |  

 | Tom       | Pawlo     | tom@m.com | 500600700 |  

 | June      | Gool      | jg@m.com  | 100299300 |  

Then “2” entries should be present

Looking at this Feature file from the Squish perspective, we can think of a Scenario as a test case and Feature as a test suite.

Next we need to actually automate these test cases with Squish. This means adding Python code with Squish and Java API to test the steps. To do that we need to create another file - steps/addressbok.py. The Python code for every step can be either recorded by Squish using Record/Playback functionality from the Squish IDE, or entered manually.

In order to run Squish test cases from a Python script we are using the Python module squishtest. More information about this module can be found in the article Using Squish as a module in other Python scripts, applications. Please remember to add the squishtest module name before every method call, as this module is not added automatically when code is generated.

from behave import *
import names
import squishtest

@given('no prior existing addressbook')
def step_impl(context):
	pass
	
@when('I create a new addressbook')
def step_impl(context):
	squishtest.activateItem(squishtest.waitForObjectItem(names.AddressBook_JMenuBar, "File"))
	squishtest.activateItem(squishtest.waitForObjectItem(names.File_JMenu, "New..."))

@then('"{number}" entries should be present')   
def step_impl(context,number):
	assert squishtest.findObject(names.AddressBook_JTable).rowcount is int(number)
	
@given('a newly created addressbook')
def step_impl(context):
	squishtest.activateItem(squishtest.waitForObjectItem(names.AddressBook_JMenuBar, "File"))
	squishtest.activateItem(squishtest.waitForObjectItem(names.File_JMenu, "New..."))

@when('adding persons to addressbook')
def step_impl(context):
    for row in context.table:
		squishtest.activateItem(squishtest.waitForObjectItem(names.Address Book_JMenuBar, "Edit"))
		squishtest.activateItem(squishtest.waitForObjectItem(names.Edit_JMenu, "Add..."))
		squishtest.type(squishtest.waitForObject(names.AddressBook_AddForename_JTextField), row['firstname'])
		squishtest.type(squishtest.waitForObject(names.AddressBook_AddSurname_JTextField), row['lastname'])
		squishtest.type(squishtest.waitForObject(names.AddressBook_AddEmail_JTextField), row['email'])
		squishtest.type(squishtest.waitForObject(names.AddressBook_AddPhone_JTextField), row['phone'])
		squishtest.clickButton(squishtest.waitForObject(names.AddressBook_AddOK_JButton))
addressbook.py

Next create one more file where we may define code to run before and after certain events: environment.py. Now create the function before_scenario, where we define information about the Application Under Test, Object Map and Test Results.

import squishtest

def before_scenario(context, scenario):
	squishtest.setTestResult( "xml2", "results.xml" )
	squishtest.testSettings.setWrappersForApplication( "AddressBook.class", "Java" )
	squishtest.startApplication( "AddressBook.class" )
environment.py

Execution

We just need to start squishserver and we are ready to execute our test suite. To do that simply run:

behave addressbook

Output:

Feature: Filling of addressbook # addressbook.feature:1

  Scenario: Initial state of created addressbook  # addressbook.feature:3
    Given no prior existing addressbook           # steps/addressbook.py:4 0.000s
    When I create a new addressbook               # steps/addressbook.py:8 1.216s
    Then "0" entries should be present            # steps/addressbook.py:13 0.059s


  Scenario: State after adding two entries  # addressbook.feature:8
    Given a newly created addressbook       # steps/addressbook.py:17 1.205s
    When adding persons to addressbook      # steps/addressbook.py:22 5.197s
      | firstname | lastname | email     | phone     |
      | Tom       | Pawlo    | tom@m.com | 500600700 |
      | June      | Gool     | jg@m.com  | 100299300 |
    Then "2" entries should be present      # steps/addressbook.py:13 0.003s

1 feature passed, 0 failed, 0 skipped
2 scenarios passed, 0 failed, 0 skipped
6 steps passed, 0 failed, 0 skipped, 0 undefined
Took 0m7.681s

Now let us see how output from our execution will look when an error in the application is found. To simulate such case the address book.feature file was modified to expect 1 entry when a new addressbook is created.

Output from execution with failures:

Scenario: Initial state of created addressbook  # addressbook/addressbook.feature:3
    Given no prior existing addressbook           # addressbook/steps/addressbook.py:4 0.000s
    When I create a new addressbook               # addressbook/steps/addressbook.py:8 1.214s
    Then "1" entries should be present            # addressbook/steps/addressbook.py:13 0.053s
      Traceback (most recent call last):
        File "/Library/Python/2.7/site-packages/behave/model.py", line 1037, in run
          match.run(runner.context)
        File "/Library/Python/2.7/site-packages/behave/model.py", line 1430, in run
          self.func(context, *args, **kwargs)
        File "/Users/tomasz/behave/addressbook/steps/addressbook.py", line 15, in step_impl
          assert squishtest.findObject(names.AddressBook_JTable).rowcount is int(number)
      AssertionError

Additionally to output from Behave execution, for every executed Scenario, Squish results.xml file is generated, where all Squish logs,errors and warnings can be found.

Additional notes specific to Mac OS X

  1. To run tests the following environment variables need to be defined:

    export SQUISH_USE_AWT=1
    export SQUISH_DIR=<location of Squish>
    export VERSIONER_PYTHON_PREFER_32_BIT=yes
    export PYTHONPATH=$SQUISH_DIR/lib
    export DYLD_LIBRARY_PATH=$SQUISH_DIR/lib:$SQUISH_DIR/perl/lib/5.10.1/darwin-thread-multi-2level/CORE
  2. On 64bit systems, Behave shall be run in 32bit mode, because squishtest module is 32bit. In that case, define the environment variable VERSIONER_PYTHON_PREFER_32_BIT=yes.