In a previous blog on TDD I advocated that you should define your test on a high level. One of the reasons why you should use a high level language is that you can use your tests not only for your API but also for for instance your UI. In this blog I will show how you can use the same test pages in FitNesse to test both the API and UI of your system.
Using Slim Scenario fixtures this can be implemented fairly easy. Scenarios are Slim tables that can be called form other Slim tables. For a step by step tutorial on how to apply Scenarios I recommend watching Bob Martin’s screencast.
With a few additions to Bob Martin’s video you can use that test for both API and UI.
Lets say we have the following BDD style test:
given a current maximum capacity of 2 and 1 items already planned then I can lower the maximum capacity to 1 then I can not lower the maximum capacity to 0
Using scenarios this would end up in the following FitNesse markup:
!|scenario|given a current maximum capacity of |maxCapacity| and |itemsPlanned| items already planned| |set maximum capacity to|@maxCapacity| |plan|@itemsPlanned|items| !|scenario|then I can lower the maximum capacity to|newMaxCapacity| |ensure|set maximum capacity to|@newMaxCapacity| !|scenario|then I can not lower the maximum capacity to|newMaxCapacity| |reject|set maximum capacity to|@newMaxCapacity| !|script|MaxCapacityApiDriver| !|script| |given a current maximum capacity of |2| and |1| items already planned| |then I can lower the maximum capacity to |1| |then I can not lower the maximum capacity to |0|
And the fixture would look like this:
public class MaxCapacityApiDriver {
private Capacity capacity = new Capacity();
public boolean setMaximumCapacityTo(int max) {
try {
capacity.setMax(max);
return true;
} catch(MoreItemsThanCapacityException e) {
return false;
}
}
public void planItems(int items) {
for(int x=0; x<items; x++) {
capacity.plan();
}
}
}
Now if we look at the example we could also apply this to our maintenance user interface where the admin can actually change the max capacity.
Using selenium-rc this can be implemented fairly easy. I choose to keep the FitNesse pages the same, the only thing I change is the driver that is used.
Step 1. Extract Scenario
we do not want to duplicate the scenarios so we extract the scenarios to a seperate page. We can then include this page using the !include directive in FitNesse.
Our test FitNesse markup now looks like this:
!include ScenarioPage !|script|MaxCapacityApiDriver| !|script| |given a current maximum capacity of |2| and |1| items already planned| |then I can lower the maximum capacity to |1| |then I can not lower the maximum capacity to |0|
Step 2. Extract Test
Wait a minute. We also want to reuse our BDD style test. Let’s do the same trick as we did using the ScenarioPage.
After extracting the Test our FitNesse markup looks like this:
!include ScenarioPage !|script|MaxCapacityApiDriver| !include TestPage
Step 3. Implement Driver
Now all we have to do is implement the UI driver.
!include ScenarioPage !|script|MaxCapacityUiDriver| !include TestPage
This is how the fixture code could look like using selenium-rc, I left out some plumbing for readability
public class MaxCapacityUiDriver {
private Selenium selenium;
public boolean setMaximumCapacityTo(int max) {
selenium.open("/capacity/change");
selenium.type("maximum", String.valueOf(max));
selenium.click("changeMaxButton");
selenium.waitForPageToLoad(TIMEOUT);
return selenium.isTextPresent("successfully changed capacity to: "
+ max);
}
public void planItems(int items) {
selenium.open("/items");
for (int x = 0; x < items; x++) {
selenium.click("planItem");
selenium.waitForPageToLoad(TIMEOUT);
}
}
}
Using FitNesse scenarios and extracting pages to include them is very handy when you want to re-use some tests. Please keep in mind that it is not wise to test your application code through your UI, so apply this carefully. I tend to pick only a few scenarios to see if the UI and API communicate properly.
