DuplicationBetweenCustomerAndProgrammerTests
Question from a post on the yahoo fitnesse group:
Say you got a person object and requirements state that the person passport id must be unique. Would you then write a test for that as a customer. Surely the same test will exist on a unit level. Also how would you specify it? Through an "Add Person" column fixture, adding the same person twice, or through a "Unique Person Passport ID" Test?
Answer from David Chelimsky
There's going to be some duplication between the customer tests and the programmer tests and that's perfectly OK. They serve different purposes. Assuming you're writing tests first, use fitnesse to describe how a customer uses the system. Then use xUnit to describe how some code can use other code.
In your passport example, you'd likely use a AddPersonFixture (ColumnFixture) and a PeopleFixture (RowFixture) on a page with a title "TestUniquePassports". This page would describe the requirement in free text and then use the fixtures as part of the description. Here's what the fitnesse page might look like:
If you're just starting to write the system and this is the first test you tackle, you'd write the AddPersonFixture and the PeopleFixture (both of which will likely be used all over the place). Both fixtures would need to tap into application classes that know how to manage users, so you'd write a call to your UserDatabase (for example), your IDE will tell you that it doesn't exist so you have to make one. Different people handle the next step differently. I'm really particular about having green bars in xUnit very regularly, and I'm very particular about writing code only to satisfy failing unit tests. The fact that the fitnesse test is failing tells me that my application doesn't support a requirement now. I can live with that for the time it takes to get that requirement supported. I can't live with red bars in my unit tests for that long because they are my safety net.
So what I'll do is comment out the fixture code that calls on my UserDatabase so that my code can compile and then start to develop just enough functionality to satisfy the needs of my fixtures, doing it all using TDD best practices which you can read about in a variety of places. Once enough code is unit tested and developed to uncomment the code in my fixtures, I'll do so. The likelihood is that I would get to the point where you can add users to the database and then get all of the users - nothing more. So I'll uncomment the fixture code, run the test and it fails because I didn't handle the error yet.
Now here's were a lot of people make what I view as the wrong decision. You've got a failing test in fitnesse, and it's interacting w/ your UserDatabase through your fixture, so the tempation is to just use the fitnesse test as your safety net and start coding. Here's the flaw in that thinking. The fitnesse page itself does not compile with your code. You're going to take a lot of steps to get that fitnesse test to pass and you're going to keep getting deceitful green bars that lie to you telling you that your code is sound. The problem is that you won't discover that you introduced a bug until you run the fitnesse tests, which you won't do for a variety of reasons, nor should you - they're not there to verify soundness of your code, they're there to verify requirements.
So write your unit tests even though exactly the same stuff is being tested in fitnesse (maybe even with virtually the same test).
FYI - the fitnesse example is a shameless plug for the exception[Type: "message"] syntax, which is only available in .NET Fitnesse at the moment. You can learn more about it and other syntax based cell handlers at http://fitnesse.org/FitNesse.DotNet.SuiteAcceptanceTests.SuiteCellHandlerTests.
[.FrontPage] [.RecentChanges]
Say you got a person object and requirements state that the person passport id must be unique. Would you then write a test for that as a customer. Surely the same test will exist on a unit level. Also how would you specify it? Through an "Add Person" column fixture, adding the same person twice, or through a "Unique Person Passport ID" Test?
Answer from David Chelimsky
There's going to be some duplication between the customer tests and the programmer tests and that's perfectly OK. They serve different purposes. Assuming you're writing tests first, use fitnesse to describe how a customer uses the system. Then use xUnit to describe how some code can use other code.
In your passport example, you'd likely use a AddPersonFixture (ColumnFixture) and a PeopleFixture (RowFixture) on a page with a title "TestUniquePassports". This page would describe the requirement in free text and then use the fixtures as part of the description. Here's what the fitnesse page might look like:
The passport is the unique identifier for people in the system, and the system should not allow two user records with the same passport.
Say for example you add a person:
Now add the same person again. The system should inform the user that this passport is already present in the system.
Now add a different person, but use the same passport. Again, the system should inform the user that this passport is already present in the system.
And then make sure that there's only one user in the system.
Say for example you add a person:
| add person | |||
| first name | last name | passport | submit() |
| Joe | Smith | 987654321 | ok |
Now add the same person again. The system should inform the user that this passport is already present in the system.
| add person | |||
| first name | last name | passport | submit() |
| Joe | Smith | 987654321 | exception[PassportExistsException: "Duplicate passport"] |
Now add a different person, but use the same passport. Again, the system should inform the user that this passport is already present in the system.
| add person | |||
| first name | last name | passport | submit() |
| Jane | Roe | 987654321 | exception[PassportExistsException: "Duplicate passport"] |
And then make sure that there's only one user in the system.
| people | ||
| first name | last name | passport |
| Joe | Smith | 987654321 |
So what I'll do is comment out the fixture code that calls on my UserDatabase so that my code can compile and then start to develop just enough functionality to satisfy the needs of my fixtures, doing it all using TDD best practices which you can read about in a variety of places. Once enough code is unit tested and developed to uncomment the code in my fixtures, I'll do so. The likelihood is that I would get to the point where you can add users to the database and then get all of the users - nothing more. So I'll uncomment the fixture code, run the test and it fails because I didn't handle the error yet.
Now here's were a lot of people make what I view as the wrong decision. You've got a failing test in fitnesse, and it's interacting w/ your UserDatabase through your fixture, so the tempation is to just use the fitnesse test as your safety net and start coding. Here's the flaw in that thinking. The fitnesse page itself does not compile with your code. You're going to take a lot of steps to get that fitnesse test to pass and you're going to keep getting deceitful green bars that lie to you telling you that your code is sound. The problem is that you won't discover that you introduced a bug until you run the fitnesse tests, which you won't do for a variety of reasons, nor should you - they're not there to verify soundness of your code, they're there to verify requirements.
So write your unit tests even though exactly the same stuff is being tested in fitnesse (maybe even with virtually the same test).
FYI - the fitnesse example is a shameless plug for the exception[Type: "message"] syntax, which is only available in .NET Fitnesse at the moment. You can learn more about it and other syntax based cell handlers at http://fitnesse.org/FitNesse.DotNet.SuiteAcceptanceTests.SuiteCellHandlerTests.
[.FrontPage] [.RecentChanges]