Behaving
Behavior-Driver-Development for multi-user web/email/sms applications
Install / Use
/learn @ggozad/BehavingREADME
behaving
behaving is a web application testing framework for Behavior-Driven-Development, based on behave and splinter.
behave is written in Python and is similar to Cucumber. behaving adds the step-libraries for multi-user web/email/sms/gcm interactions, and provides the Python behaving namespace so that independent step-libraries can work together.
Please refer to behave's excellent documentation for a guide on how to use it, how to write your custom steps and make it possible to extend behaving.
Contributing
See CONTRIBUTING.rst
Hello world
Starting to use behaving is pretty easy. Inside some python module,
add your features consisting each of one or more scenarios. These
features are Gherkin language files with an extension of .feature. In
the same directory you should have a steps module which imports the
behaving steps as well as your own custom steps (more on that later in
the setup_ section) . Here's a basic example:
Feature: Text presence
Background:
Given a browser
Scenario: Search for BDD
When I visit "http://www.wikipedia.org/"
And I fill in "search" with "BDD"
And I press "go"
Then I should see "Behavior-driven development" within 5 seconds
Email, SMS & GCM (Google Cloud Messaging)
While the web is the focus of behaving, it also includes simple mocks for a mail, SMS and a GCM server. These come with a small collection of steps allowing you to do things like:
Feature: Email & SMS
Scenario: Click link in an email
Given a browser
When I send an email to "foo@bar.com" with subject "Hello" and body "Try out this website at http://google.com"
And I click the link in the email I received at "foo@bar.com"
Then the browser's URL should be "http://google.com/"
Scenario: Receive SMS with body
When I send an sms to "+4745690001" with body "Hello world"
Then I should receive an sms at "+4745690001" containing "world"
Scenario: Receive GCM Notification
When I send a gcm message "{"to":"deviceID", "data": {"message": "Foo Bar", "badge": 6}}"
Then I should receive a gcm notification at "deviceID" containing "{'data': {'message': 'Foo Bar'}}"
Typically, it will be your web application that sends email/sms/notifications and testing it comes down to configuring the application to send email/sms/notifications to the mock servers.
Personas & state
A lot of web apps today rely on multi-user interactions. To help you with those interactions, behaving uses the notion of personas. A persona within a test runs in its own instance of a browser and you can have more than one persona (and its browser instance) running concurrently. You switch among personas by calling
Given "PersonaName" as the persona
Personas are also typically implemented as simple dictionaries allowing them to carry state, save and reuse variables inside a scenario. When a persona is first invoked it is created as an empty dictionary. You can predefine personas though with set values.
Let's take the familiar LOTR characters as our test users. On setting up the test environment (details later in the setup_ section), we set up the characters basic variables we might be needing in the tests as such:
PERSONAS = {
'Frodo': dict(
fullname=u'Frodo Baggins',
email=u'frodo@shire.com',
password=u'frodopass',
mobile='+4745690001',
address: {
street: "The Shire",
zip: "4321"
}
),
'Gandalf': dict(
fullname=u'Gandalf the Grey',
email=u'gandalf@wizardry.com',
password=u'gandalfpass',
mobile='+4745690004',
address: {
street: "Rivendell street 1",
zip: "1234"
}
),
...
}
def before_scenario(context, scenario):
...
context.personas = PERSONAS
Within a test and given a persona, you can now use $var_name to access
a variable of a persona. You can also set new variables on personas. So
the following,
Given "Gandalf" as the persona
When I fill in "name" with "$fullname"
And I fill in "street" with "$address.street"
And I set "title" to the text of "document-title"
And I fill in "delete" with "$title"
And I set "address.country" to the text of "country"
And I set "postaddress" to:
"""
$fullname
$address.street, $address.zip, $address.country
"""
would fill in the field with id name with Gandalf the Grey, street
with Rivendell street 1 set the variable title to the text of the
element with id document-title and reuse the variable title to fill
in the field with id delete. It would also store the value of the
field with id "country" in address[country]. The $var_name pattern
is also usable in the text received by steps that expect a body of text,
which means that the postaddress persona variable will contain
Gandalf's complete snail-mail postage address nicely formatted on
multiple lines.
Hello Persona example
Let us assume the following (coming from a real example) scenario. Crypho, is an online messaging/sharing site that provides users with end-to-end encrypted real-time communications. behaving was written to help test Crypho.
In Crypho, teams collaborate in spaces. To invite somebody in a space the invitee has to share a token with an invitor, so both can verify each other's identity.
Feature: Frodo invites Gandalf to The Shire space
Given state "the-shire"
Scenario: Frodo invites Gandalf to The Shire
Given "Gandalf" as the persona
When I log in
Before the scenarios start, the custom step Given state "the-shire"
executes. This preloads the db with data, sets up the server etc. Then
the scenario executes:
First Gandalf logs in. The step Given "Gandalf" as the persona, fires
up a browser that belongs to the persona Gandalf. The following step,
When I log in is a custom step defined as follows:
@when('I log in')
def log_in(context):
assert context.persona
context.execute_steps(u"""
When I go to Home
Then I should see an element with id "email" within 2 seconds
When I fill in "email" with "$email"
And I press "send-sms"
Then I should see "We have sent you an SMS with a security code" within 2 seconds
And I should receive an sms at "$mobile"
And "token" should be enabled
When I parse the sms I received at "$mobile" and set "Your Crypho code is {token}"
And I fill in "token" with "$token"
And I fill in "password" with "$password"
And I press "login"
Then I should see "Crypho" within 5 seconds
""")
Observe above how the current persona (Gandalf) parses the sms it receives and saves it as "token". Later Gandalf reuses it to fill in the two-factor authentication field.
Now that Gandalf is logged in, the test proceeds with Frodo. Frodo will log in, and invite Gandalf to a private space.
Given "Frodo" as the persona
When I log in
And I click the link with text that contains "My spaces"
And I click the link with text that contains "The Shire"
And I press "invite-members"
Then I should see "Invite members" within 1 seconds
When I fill in "invitees" with "gandalf@wizardry.com"
And I fill in "invitation-message" with "Come and join us!"
And I press "send-invitations"
Then I should see "Your invitations have been sent" within 2 seconds
Once the invitations are sent we switch back to Gandalf's browser, who should have received a notification in his browser, as well as an email. He then proceeds to send an sms to Frodo with the token who completes the invitation.
Given "Gandalf" as the persona
Then I should see "Your invitations have been updated" within 2 seconds
And I should receive an email at "gandalf@wizardry.com" containing "Frodo Baggins has invited you to join a private workspace in Crypho"
When I click the link with text that contains "Invitations"
And I click the link with text that contains "Pending invitations"
Then I should see "Come and join us!"
When I set "token" to the text of "invitation-token"
And I send an sms to "45699900" with body "$token"
Given "Frodo" as the persona
Then I should receive an sms at "45699900"
When I set "FrodoToken" to the body of the sms I received at "45699900"
And I click the link with text that contains "Invitations"
And I click the link with text that contains "Enter authorization token"
And I fill in "auth-token" with "$FrodoToken"
And I press "Submit"
Then I should see "The invitation has been accepted." within 5 seconds
And I should see "Gandalf the Grey has joined the space, invited by Frodo Baggins" within 10 seconds
You can see the test in action on video here.
Setting up a test environment
Start by installing behaving by using either pip or easy_install.
This will also install dependencies and create the behave script with
which you invoke your tests. If you prefer using buildout, clone the
package itself from its repository, it contains already a buildout
configuration.
Typically you will be having a folder containing all your features and steps. For example a directory structure like
