Introducing DSL Adapter for Generic Fixture
March 26, 2008 by anubhava
Syntax of defining DSL: It is basically a mapping of DSL and Java method calls. All of your DSL mappings will be in a FitNesse table with DSLAdapter as the class name, appearing in first column of the first row
Basic Syntax is: DSL follwed by Actual Java Method call
Each argument to DSL & Java method appears in a separate table cell. All arguments of DSL appear as %. All the arguments to a Java method call are defined as positional parameters of DSL such as {1}, {2},…{n} delimited by comma character “,”.
So for a method called max(arg1, arg2) if you want to define a DSL such as “get the max of 2 numbers num1 and num2″ then it would look like this:
!| DSL Adapter |
| get the max of 2 numbers | % | and | % | max | {1}, {2} |
% is just a place holder over here for arguments and {1} is the first argument in DSL and {2} is second parameter in DSL. Once defined now to use this DSL in writing tests simply write this:
| get the max of 2 numbers | 25 | and | 28 | |
Once you take a look at some more examples it will be much more clear how to create and modify your own DSL.
Let’s take a look at some more examples:
While writing web tests using Selenium there often is a need for pausing the test for 1 or 2 sec to let Selenium do frame loading (esp needed in Frames based testing where main page has loaded but some iframe still needs time to load). Selenium doesn’t have a pause method and ONLY way to pause it is by calling sleep(long ms) method in java.lang.Thread. Testers can simple write: | java.lang.Thread.sleep | 2000 | to get 2 second pause but since it is a pure Java call and sometimes too technical for test authors it is better to define a DSL for that using following syntax:
!| DSL Adapter |
| pause the test for | % | seconds | java.lang.Thread.sleep | {1}000 |
Having defined this DSL now test authors will just need to write this in their web tests:
| pause the test for | 2 | seconds |
which is much more readable and user friendly.
Now lets take a look at my DSL for Selenium API. Define this DSL in a SetUp page to make it available for all the test pages.
!| DSL Adapter |
| user starts the browser | start | |
| user opens the URL | % | open | {1} |
| page has the title | getTitle | |
| user types | % | into | % | field | type | {2}, {1} |
| user sees the text on the page | % | isTextPresent | {1} |
| page has an element named | % | isElementPresent | {1} |
| page loads in less than | % | seconds | waitForPageToLoad | {1}000 |
| page has URL | getLocation | |
| user clicks on the submit button | click | //button[@type='submit'] |
| user clicks on the link named | % | click | link={1} |
| user clicks on the button named | % | clickAt | {1},”" |
| get text from element named | % | getText | xpath=id(’{1}’) |
| get xpath text from address | % | getText | xpath={1} |
| user removes the cookie named | % | at path | % | deleteCookie | {1}, {2} |
| user selects an option | % | from the drop-down | % | select | {2}, label={1} |
| pause the test for | % | seconds | java.lang.Thread.sleep | {1}000 |
| user stops the browser | stop | |
Using above DSL my Google web test will look like this. First add this test table to start Selenium test in SetUp page:
!define ds {com.thoughtworks.selenium.DefaultSelenium}
!define site {http://www.google.com}
!| Generic Fixture | selenium=${ds} | localhost | 4444 | *pifirefox | ${site} |
| user starts the browser |
And write this test table in your main test page GoogleTest:
!define q {fitnesse generic fixture}
!| Generic Fixture | selenium= |
| user opens the URL | http://www.google.com |
| page has the title | | Google |
| page has an element named | q | | true |
| page has an element named | btnG | | true |
| user types | ${q} | into | q | field |
| user clicks on the button named | btnG |
| page loads in less than | 5 | seconds |
| page has the title | | ${q} - Google Search |
| user clicks on the link named | Next |
| page loads in less than | 5 | seconds |
| user clicks on the link named | Next |
| page loads in less than | 5 | seconds |
| user sees the text on the page | Results 21 - 30 | | true |
and finally this small test table in TearDown page to stop the Selenium test:
!| Generic Fixture | selenium= | | user stops the browser |
Few important things to be noted here are:
- A new instance of Selenium is being created in SetUp page and being used in main page and TearDown page using a variable selenium=.
- By using a variable to store Selenium instance and reusing it, you can move common initialization code in SetUp and common cleanup code in TearDown page. Also it will make sure that in the event of an exception being thrown (and that may happen often in web testing) your cleanup (TearDown page) will ALWAYS be called.
- Test tables are not calling any of the Selenium APIs, only DSL is being used to direct Selenium run all the test steps.
- Even though Selenium’s waitForPageToLoad method expects you to pass milli seconds, using DSL we pass seconds value only by appending 000 in the argument. Using this approach you can always hide the complexity of the method arguments from the test authors.
- Customizing this DSL is super easy. Lets say some user doesn’t like the text user opens the URL and wants user opens a page instead. In that case just go to the above FitNesse’s SetUp page and edit the table that starts with DSL Adapter and replace the text so it will become like this:
| user opens a page | http://www.google.com | - If the underlying class (Selenium in this case) adds some new APIs with its new release, any other Fixture implementation approach would require a corresponding code change and new release. Whereas this DSL adapter just needs couple of new lines in a FitNesse SetUp page to define new “mapping” between DSL and new API calls.
Good Layout and design. I like your blog. I just added your RSS feed to my Google News Reader. .
Jason Rakowski