Over the last year or so, interest in and development for Node.js has accelerated dramatically. There is no shortage of excitement and promise in this burgeoning community and for many of the same reasons cited by others, about six months ago we decided it made sense to dive in ourselves.
Coming from a committed testing background on multiple web frameworks, including Rails, we are strong adherents to BDD. Naturally the first thing we did when spinning up on the Node stack was look around for good BDD acceptance testing tools. Not finding anything that fit for us, we went ahead and rolled our own. In this post, we’ll introduce GivenWhenThen, a DSL and runner that allows anyone to easily construct acceptance tests with BDD semantics in straightforward, sentence-like statements and then run them against Sauce Labs.
Writing a Story
Although you can also directly access the Selenium RemoteWebDriver that is under the covers, GivenWhenThen is primarily intended to provide a DSL for writing executable stories in the Dan North format. To write a story, begin with a BDD description in a xxx_test.coffee file:
story 'Executing a Google search', """ As a human I want to perform a search So that I can access the world's information """, ->
And then add one or more scenarios:
scenario "Search for info about Node.js", (browser) ->
browser
.given "I am on the homepage", ->
browser.step(steps.visitHomepage)
.when "I enter search terms", ->
browser.typeInElement('q', 'nodejs', using:'name')
.and "submit the search", ->
browser.clickElement('btnG', using:'name')
.then "I see search results", ->
browser.assertTextPresent('results')
.and "the results contain information about nodejs", ->
browser
.assertTextPresent('node.js')
.assertTextPresent('nodejs.org')
- Given: Set up the initial conditions for the scenario.
- When: Take the action the scenario is testing.
- Then: Assert the conditions expected after taking the tested action.
Each step contains one or more chained calls to WebDriver commands in the form of browser.someCommand.
Each step (given, when, then) can have an arbitrary number of and steps following it (see above example).
Steps
browser.step():
scenario "Search for info about Node.js", (browser) ->
browser
.given "I am on the homepage", ->
browser.step(steps.visitHomepage)
*_steps.coffee files. Multiple steps per file can be defined as follows:
steps.visitHomepage = (browser) -> browser.get 'http://www.google.com'
Multiple steps files can be created to organize your steps in a manageable way.
Configuration
- Overall story and Sauce Labs configuration.
- Browser / OS definitions. Stories will be run against each browser/os configuration defined.
For example:
config.credentials =
'username': 'sauce_labs_username'
'access-key': 'sauce_labs_access_key'
config.settings =
'max-duration': '180'
config.browsers = [
{
'platform': 'VISTA'
'browserName': 'firefox'
'version': '7'
}
{
'platform': 'LINUX'
'browserName': 'firefox'
'version': '7'
}
]
Subtitles In Test Videos
Design Background
In addition to being built on top of Sauce Labs and Selenium 2, GivenWhenThen is heavily influenced by the BDD movement.
While it is also clearly influenced by Cucumber, aficionados will surely notice differences. Most obviously, in GivenWhenThen there is no prose-to-code translation step. Cucumber uses the Gherkin language, which parses the prose into features, scenarios, and steps.
In GivenWhenThen, the DSL is embedded directly in the host language. We do realize there is real benefit to the full Cucumber vision – including Gherkin – but we took a leaner approach to getting the BDD structure without completely porting Cucumber. To us, the core power of the BDD approach comes from the conceptual framework Dan North developed. Just stating the story and scenario in that formal way and using the “given, when, then” flow gives powerful structure and coherence to your specifications.

