Two powerful tools for communicating requirements from your customers, and testing those requirements, are Fitnesse and Selenium. Fitnesse, a wiki encapsulating the Framework for Integration Tests (aka FIT), enables customers to write sentence-like tests which can be mapped to the underlying system. Selenium drives a browser without all of the fragile mouse coordinate testing you get from a lot of testing tools (ala WinRunner).
While there’s been a lot of good posts on Fitnesse and Selenium, none of them seemed to fit in to exactly what we need, which was a way for our customers to harness the power of Selenium in the acceptance tests they are writing. So, using the DoFixture
and the Selenium Remote Control Java API, we were able to hack together a custom Fitnesse fixture which reads surprisingly well.
First, you’ll need to set up Selenium. I’m going to be writing a tutorial on it soon, but basically just download the Selenium Remote Control, unzip it, and start up the server. With the server running, you can write Java code which will make calls to the server and interact with the browser.
Now, onto our tests. We wanted to give our customers the ability to write tests like:
|The user navigates to the URL|http://www.google.com|
|The page has the title|Google|
|The page has an element named|q|
|The page has an element named|btnG|
|The user Types|Cory Foy|in the field named|q|
|The user clicks on the button named|btnG|
|The user closes the browser|
This is the wiki-syntax from Fitnesse, and something our customers are familiar with writing. One of the nice things about the DoFixture in Fitnesse is that it knows how to convert these to java method calls. For example, this:
|The user navigates to the URL|http://www.google.com|
gets mapped to:
public boolean TheUserNavigatesToTheUrl(String url) {}
I’ve put the Fitnesse fixture up for download. You’ll need to compile it and put it on the classpath, along with the Selenium java client jar. To use it, you’ll have to make sure that the SeleniumRunner is the first table in your test, and then initialize it using the Selenium properties. Ideally, you could just move this up to a SetUp method, so your user wouldn’t see it.
|!-com.cornetdesign.fitnesse.SeleniumRunner-!|
|Set server to|localhost|and port to|4444|and browser to|*firefox|and domain to|http://www.google.com|
The above puts the Fitnesse test into Flow mode, and then initializes the Selenium runner to point at localhost:4444 using the firefox browser, and having the URLs go against the domain google.com.
I’m going to be doing some more work with this, so feedback is welcome. I hope to get a good Selenium tutorial up soon.
Looking nice! keep up the nice work.
I’m new to Fitnesse and have’t used Selenium yet. I look forward to seeing more examples.
— Rick
Although I really like the idea, you’re going to end up with very wordy tests this way. “Clear customer language” does not have to mean “redundant, repetitive language.” As you get more and more customer tests, it is important that they be succinct, so that the test reader can sort out the noise from the thing actually being tested.
So, for example, I would change “The user navigates to the URL” to be something like “Navigate to”. I would change “|The user Types|Cory Foy|in the field named|q|” to “|type|Cory Foy|in field|q|” These are still just as readable but more succinct.
— Jeff Nielsen
Digital Focus
Thanks Jeff. We’ve got a similar discussion going on at the XP list.
I know we could make them more terse, but they don’t read like good user stories. Most of the customers I’ve dealt with (except the technical ones) don’t mind writing out sentences, as long as they don’t have to do the pipes. In fact, in some industries, they feel duty-bound to be wordy. ;)
So, I’m trying to find a way to have clear, readable tests that don’t require the user to write a book. I don’t feel like being as terse as you have is quite it, but I’m not positive being as verbose as what I have above is quite it either. I guess I’m just stuck communicating with the customer to see which they like better.
Why not add alias functions?
public boolean navigateTo(String targetUrl) {return theUserNavigatesToTheUrl(targetUrl);}
Thanks for the nice post and getting us started Cory.
While running the application, I landed into these errors. Do you know why? I have scratched my head a lot and couldnt get along any further. Please help. Thanks
lasspath: C:\Documents and Settings\dsharda\workspace\Fitness\fitlibrary.jar
classpath: C:\Documents and Settings\dsharda\workspace\Fitness\fitnesse.jar
classpath: C:\Documents and Settings\dsharda\workspace\Fitness\SeleniumRunner.jar
classpath: C:\Documents and Settings\dsharda\workspace\Fitness\selenium-java-client-driver-tests.jar
classpath: C:\Documents and Settings\dsharda\workspace\Fitness\selenium-server.jar
classpath: C:\Documents and Settings\dsharda\workspace\Fitness\selenium-java-client-driver.jar
SeleniumRunner
Set server to Missing method: public TypeOfResult setServerToAndPortToAndBrowserToAndDomainTo(Type1 arg1, Type2 arg2, Type3 arg3, Type4 arg4) { } in class SeleniumRunner localhost and port to 4444 and browser to *firefox and domain to http://www.google.com
The user navigates to the URL Missing method: public TypeOfResult theUserNavigatesToTheURL(Type1 arg1) { } in class SeleniumRunner http://www.google.com
Got it to work.. Thanks
How can I get the Assert thing working with Fitnesse. I want to know why a particular test failed and with boolean as the return type for a test I can’t get the stack trace.
Any idea? Thanks-
Cory,
I have also been thinking about how to translate what FIT bring to unit testing to
user interface web testing. So far I did not find any great answer.
But I do not see myself writing and maintaining 1000 of lines using Fitness script similar to the one from your post..
Here is the test that you wrote using InCisif.net and C#.
For now I will stick with that but I will continue to read you excellent blog.
using ( InCisif.net.Library.Test t = new InCisif.net.Library.Test(Language.CSharp, this) ) {
Page.URL = @”www.google.com”;
Page.WaitForPage(@”re:/www.google.com\/$”);
Page.Control(“q”).Text = @”Cory Foy”;
Page.Control(“btnG”).Click();
Page.WaitForPage(@”/search”); // URL:’http://www.google.com/search’
t.Passed = true;
}
I started the selenium server & ran the java program to open google.com, but in the selenium server it gets stuck up at trying to get the page..Would be really grateful if u could help me thru
[c:\documents and settings\mthahir]java -jar D:\Selenium\server\selenium-server.jar
application/xhtml+xml
Jun 26, 2007 7:58:11 PM org.mortbay.http.HttpServer doStart
INFO: Version Jetty/0.9.0
Jun 26, 2007 7:58:12 PM org.mortbay.util.Container start
INFO: Started HttpContext[/,/]
Jun 26, 2007 7:58:12 PM org.mortbay.util.Container start
INFO: Started HttpContext[/selenium-server,/selenium-server]
Jun 26, 2007 7:58:12 PM org.mortbay.util.Container start
INFO: Started HttpContext[/selenium-server/driver,/selenium-server/driver]
Jun 26, 2007 7:58:12 PM org.mortbay.http.SocketListener start
INFO: Started SocketListener on 0.0.0.0:4444
Jun 26, 2007 7:58:12 PM org.mortbay.util.Container start
INFO: Started org.mortbay.jetty.Server@4741d6
Jun 26, 2007 7:58:15 PM org.mortbay.util.FileResource
INFO: Checking Resource aliases
GET: cmd=open&1=http%3A%2F%2Fwww.google.com%2Fwebhp
I’m getting the same outcome as the other anonimous poster,”The user navigates to the URL Missing method: public TypeOfResult theUserNavigatesToTheURL(Type1 arg1) { } in class SeleniumRunner” Can you post how to fix this?
Thanks
Thank you so very much. This was exactly what I needed to make FitNesse work with Selenium!
Cory, what about Selenium calling Fitnesse – how does this work? Also, would you know how to pass parameters from Selenium to Fitnesse and vice versa? Thanks.
While running the application, I landed into these errors.
Set server to Missing method: public TypeOfResult setServerToAndPortToAndBrowserToAndDomainTo(Type1 arg1, Type2 arg2, Type3 arg3, Type4 arg4) { } in class SeleniumRunner localhost and port to 4444 and browser to *firefox and domain to http://www.google.com
The user navigates to the URL Missing method: public TypeOfResult theUserNavigatesToTheURL(Type1 arg1) { } in class SeleniumRunner http://www.google.com
Hi,
Please tell me about the Selenium Runner you are mentioning. I dint see this file in the downloaded one.So I have not included that row in the fitnesse table.
I tried the same example .I have added the external jars of “selenium server.jar” and “selenium-java-client-driver.jar” also I have specified the class definition in the fixture like below
import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.SeleniumException;
import fitlibrary.DoFixture;
But still I am gettig the below error.
java.lang.NoClassDefFoundError: com/thoughtworks/selenium/Selenium
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at fit.FixtureLoader.loadFixtureClass(FixtureLoader.java:67)
at fit.FixtureLoader.instantiateFixture(FixtureLoader.java:58)
at fit.FixtureLoader.instantiateFirstValidFixtureClass(FixtureLoader.java:88)
at fit.FixtureLoader.disgraceThenLoad(FixtureLoader.java:39)
at fit.Fixture.loadFixture(Fixture.java:147)
at fit.Fixture.getLinkedFixtureWithArgs(Fixture.java:138)
at fit.Fixture.doTables(Fixture.java:73)
at fit.FitServer.process(FitServer.java:76)
at fit.FitServer.run(FitServer.java:52)
at fit.FitServer.main(FitServer.java:43)
PLease can you help?
Thanks
Getting this error –
fittest.SeleniumRunner Missing method: public TypeOfResult getFittestDotSeleniumRunner() { } OR: public TypeOfResult fittestDotSeleniumRunner() { } in class fittest.SeleniumRunner
Set server to Missing method: public TypeOfResult setServerToAndPortToAndBrowserToAndDomainTo(Type1 arg1, Type2 arg2, Type3 arg3, Type4 arg4) { } in class fittest.SeleniumRunner localhost and port to 4444 and browser to *firefox and domain to http://www.google.com
The user navigates to the URL Missing method: public TypeOfResult theUserNavigatesToTheURL(Type1 arg1) { } in class fittest.SeleniumRunner http://www.google.com
I am getting this method missing error still.
Somebody please suggest, how it works.
Hi Cory,
Nice Post.
I am new to Fitnesse.
When i am running test using with the Fitnesse i am getting follwoing errors “Missing methods: public TypeOfResult setServerToAndPortToAndBrowserToAndDomainTo”
and
“Missing methods: public TypeOfResult theUserNavigatesToTheURL”.
please post line by line integration of wrapper with the fitnesse.