Get faster with FitNesse
This is the third article in a series beginning with Busting the great TDD myth and Keep your code ignorant and lazy. If you haven’t already read these articles I recommend checking them out as I will continue to use the same code as an example.
In the last article we looked at using the Provider pattern to make a flexible data access layer. It allowed us to test our TUserRepository in isolation even though it is dependent on data. Lets breifly review that code:
type ILoadable = interface procedure AddRecord(const UserName, Password : string); end; ILoader = interface procedure Load(Target : ILoadable); end; TUserRepository = class(TInterfacedObject, ILoadable) private FUsers : TStringList; protected procedure AddRecord(const UserName, Password : string); public constructor Create(Loader : ILoader); destructor Destroy; override; function Lookup(const UserName : string) : TUser; end; TTestData = class(TInterfacedObject, ILoader) protected procedure Load(Target : ILoadable); end;
By having TUserRepository implement ILoadable and creating a stub for loading test data called TTestData we were able to write a test like this:
procedure TUserRepositoryTests.SetUp; begin TestData := TTestData.Create; FRepo := TUserRepository.Create(TestData); end; procedure TUserRepositoryTests.TestValidLookup; var user : TUser; begin user := FRepo.Lookup('fred'); CheckEquals('flinstone', user.password); end;
What’s good about this arrangement is that we’ve tested the business logic of TUserRepository independent of a data source. However we’re left the question of how to write and test the actual production code that will provide live data to TUserRepository. We could test-drive this code in a unit test but the rules of TDD tell us that a test should not need external dependencies. Also, accessing a database in a unit test would slow down the test suite. So how do we test this code?
FitNesse
FitNesse is a framework for automated acceptance testing. It has two faces: the outward (or customer facing) wiki and the internal (or developer facing) framework. We’ll tackle the task of creating and test-driving the live data provider for TUserRepository using FitNesse.
Before taking on this task, the first thing you’ll need is a copy of FitNesse. You can download the latest version at the website fitnesse.org.
The first step will be to write the test. This takes shape as a page on your FitNesse wiki:
|UserRepoDataFixture|
|UserName|Password|
|fred|flinstone|
This will be rendered on the wiki page like this:
NOTE: I won’t cover how to create wiki pages in FitNesse. For this article I created a suite called SuiteExample and added a test page called TestUserRepoDataSource.
Clicking the Test link on the left side of the page will result in an error telling you that UserRepoDataFixture could not be found. So, you guessed it, the next step is to write the UserRepoDataFixture.
You need to grab the Delphi implementation of the FitNesse framework from the website http://code.google.com/p/fit4delphi/.
The test above is based on the Row Fixture. This sort of fixture is good for testing data sets. We’ll inherit our UserRepoDataFixture from TRowFixture.
type UserRepoDataFixture = class(TRowFixture) public function query : TList; override; function getTargetClass : TClass; override; end;
The Row Fixture requires that you do two things: (1) provide a list of objects that will represent the data set and (2) provide the class type of that object.
function UserRepoDataFixture.query : TList; var user : TUser; begin user.UserName := 'fred'; user.Password := 'flinstone'; Result := TList.Create; Result.Add(user); end; function UserRepoDataFixture.getTargetClass : TClass; begin Result := TUser; end;
You’ll see that we finally needed to add the UserName field to TUser. I know that some of you have been secretly screaming that TUser should have had this field from the start. Feel better?
We can build the fixture code and run the test and see that it passes. This is good because it tells us that our fixture code is being found and executed by the Delphi fit server. We still need to do the actual work:
function UserRepoDataFixture.query : TList; var user : TUser; begin Result := TList.Create; UserDS := TUserRepoDataSource.Create; Repo := TUserRepository.Create(UserDS); for user in Repo do Result.Add(user); end;
What’s cool about this is that we didn’t have to instantiate our TUserRepository any differently than we did in our unit test. All we needed was to hand it a different implementation of ILoader.
Now some code for the TUserRepoDataSource:
type TUserRepoDataSource = class(TInterfacedObject, ILoader) private function Connect : Boolean; function MoreData : Boolean; function Current : TUser; procedure MoveNext; protected procedure Load(Target : ILoadable); end; implementation procedure TUserRepoDataSource.Load(Target : ILoadable); begin if Connect then begin while MoreData do begin Taget.AddRecord(Current.UserName, Current.Password); MoveNext; end; end; end; { SNIP }
I’ve omitted the rest of the implementation for the sake of brevity. You can tell from the Load method that this class would connect to a data source (such as a database) and loop over the result set to add each record to the ILoadable. I’ve also implied that we should add an enumerator to TUserRepository. This is one solution to the problem of how to pull the data out for the user repository. You might choose another based on your needs or your particular version of Delphi. The crux of the code is the same: load data into TUserRepository and read it back out.
Now we face the same problem as in the first article: our test relies on data. We can run this test and it might pass on our development box but fail on the build machine because our expected data is not present. One of the ways we can solve this problem is using the SetUp and TearDown facilities of FitNesse. We’ll add these pages to our SuiteExampe wiki page:
^SetUp
^TearDown
In the SetUp page we’ll add:
!|ExecSqlFixture|
|INSERT INTO User (UserName, Password) VALUES(’fred’, ‘flinstone’)|
In the TearDown page we’ll add:
!|ExecSqlFixture|
|DELETE FROM User WHERE UserName = ‘fred’|
Once that’s done out test page will render like this:
NOTE: At the time of this writing there are no database FitNesse fixtures written for Delphi. In this example I’ve used a fictional fixture called ExecSqlFixture. While it does not currently exist, writting this fixture shouldn’t be too much trouble.
Now our test assures our test data will be present before the test fixture is run and cleans up after itself at the end. This example presumes to use a SQL database as its data store. You might choose a different implementation and write different setup and tear down code.
Running the test now should find everything in order. Take some time and fiddle around with the test. Row Fixture does some neat things when you specify data that is not present and when you don’t specify data that is present.
Why is this better than what we did in the first article?
TDD prescribes that the tests should execute very fast. If they’re slow to execute then you won’t run then as often. So we move tests that are dependent on external dependencies out into the acceptance testing realm. We like this because it forces us to use a method that makes our code data source agnostic which in turn means we can test business logic in isolation. Using patterns like Factory or Builder we can take another step and write a number of different data access components and switch them in or out as we see fit.
FitNesse has considerably more benefits than what I’ve shown here. Because FitNesse is a wiki it’s easily accessible by non-tech types such as product owners and customers. In a perfect world the product owner and customer would have a suite of acceptance tests written before you started coding. These would act as executable product specifications … no more lengthy specs that get out of date and irrelevant!
I hope you enjoyed this three part series.


Hi,
thank you very much for this excellent series and for introducing FitNesse - I hadn’t heard of this! It solves the big issue I have with my unit tests - where to store the data so that it is visible and can be easily edited? I will dig deeper into this - thanks!
Andreas
Glad to hear it Andreas. Good luck! Also, check out the other FitNesse related material here.
Its so nice to see this good information in your post, I was looking the same which you post on blog, thanks now I have the thing which I was looking for, regards.
it’s really a very good and elaborate instruction!
Good! Really nice post!
There are many collections available for receiving an education these days,you can buy term paper or buy research paper which is good news for those who have not yet managed. Truthfully, essays writing is not an easy product so try make right compromise between making on yor own or to buy custom essay papers. Maybe you need help with unique audit , maybe you want help in producing a fresh intention on a circumstance that is vast and complex.
Thank you for this great series.
Very interesting and entertaining.
Well written!
It’s a precise and informative article. Things are formed symptom less. Get to know lot’s of artifact which were unbeknownst to me. It’s truly one of the most facilitative article I get ever read. Thanks for to percentage sensing for author from you. Symmetrical I bookmarked this diplomat as asymptomatic for to get supply from it in incoming.
essay writing service will help you in learning and save your time
Thank ou so much for sharing this codes. This is a good resource.
Thank you very much. I am wonderring if i can share your article in the bookmarks of society,Then more friends can talk about this problem.
Good article, thanks to the author to share.welcome come to
http://www.footballworldcupjerseys.com.
it was a wonderful chance to visit this kind of site and I am happy to know. thank you so much for giving us a chance to have this opportunity!
An amazing machine, you deserve to 1. These fantastic shoes can be a brilliant and attractive. Enjoy and of itself.
Many thanks for useful information!
I don’t usually reply to posts but I will in this case.
boring. i have seen these styles before. not original at all.
We sincerely got a kick out of your post. It appears like you have really put a good amount of effort into your post and this world need a lot more of these on the net these days. I don’t have much to say in retort, I only wanted to comment to reply well done.
Nice article, thanks for sharing this information.
Good to know that this topic is being covered also in this web site.
Truly impressive post about “Get faster with FitNesse”.
I found your website perfect for my needs. It contains wonderful and helpful posts. I have read most of them and got a lot from them.
There were a lot of talks just about the custom term papers service. However, students would understand lots just about the essays writing service ordering example essays there.
Thanks for the live-tweeting of the hearings! I appreciated being able to keep up with the hearings, and your context.
It is also the only facility on the Eastern Seaboard that can handle the massive new Moncler BRANSON Down Jacket Grey_for Men of LNG tankers. Irving is just as optimistic and sees itself as a potential market for Canaport gas. There’s room on-site to build a gas-fired generating plant, which Goodwin says could export electricity to the U.S. and provide a base-load for wind farms and turbines harnessing the Bay of Fundy’s mighty tides., Irving has provincial approval to test undersea turbines at 11 sites along the New Brunswick coast. The last piece of the puzzle is getting electricity to market. In March, Premier Graham and Maine Moncler black nylon down ‘Everest’ zip jacket John Baldacci joined forces to promote an “energy corridor” of transmission towers and pipelines through the state, to spur development and better connect New Brunswick energy producers to their customers. The province is desperate to see the project go through, but the proposal set offa political firestorm in Maine, where industrial customers, energy companies and unions banded together to force Moncler STRIPED TECHNICAL MENUIRE SPORT JACKET to ensure the state gets the best deal possible. In May, the state struck a committee to investigate Maine’s energy needs and to review proposals. A report, recommending ways to maximize economic benefits for the state, is due in early December. All this is forcing the company to act in a very unIrving-like way-discussing its plans and inviting public consultation and collaboration. Presenting a plan “developed within the walls of our company” is not an option, says Goodwin, who outlined Irving’s proposals to the committee in Moncler Nantes Hooded Down Coat. “The only project that will be right for the regionand ultimately, by extension, right for our company-will be one where everybody who has a stake in it has weighed in and has had a chance to shape it.”
For this matter, once I discussed with one of my friends, not only about the content you talked about, but also to how to improve and develop, but no results. So I am deeply moved by what you said today.
I tried to think so, but i found it was not as the same in the actual process. As you mentioned, I still have doubts, but really thank you for sharing!
Things are formed symptom less. Get to know lot’s of artifact which were unbeknownst to me. It’s truly one of the most facilitative article I get ever read. Thanks for to percentage sensing for author from you. Symmetrical I bookmarked this diplomat as asymptomatic for to get supply from it in incoming.
Also, accessing a database in a unit test would slow down the test suite. So how do we test this code?
Nice article, thanks for sharing this information.