For me unit tests should not deal with the database, integration tests deal with the database.
Integration tests that deal with the database should in practice have a empty database with a tear up and tear down approach, using a transaction based approach is quite a good way to go (i.e. create a transaction on setup and rollback on tear down).
What your friend sounds like they want to do is test from a 'regression' point of view, i.e. have real data there and see how the system reacts, after all no system is perfect and there can usually be bad data lying around somewhere that provide some quirks to your domain model.
Your best practices are the way to go, and what I tend to do, is if I find a scenario for bad data, write an integration test with a setup up and tear down with that exact scenario.
It personally sounds as if your Acceptance tests have encompassed properties of Integration tests and that you are trying to "kill two birds with one stone" as the saying goes.
In the traditional Waterfall model a single Acceptance test should determine if a single requirement has been met. If developing based on a strict SRS document, you may find that even basic input validation is explicitly defined and by the nature of Acceptance testing it needs to be manually tested to be verified.
In the Agile model however the Acceptance Tests verify a single user story, a high level test to verify a user high level stakeholder business need. Typically in the Agile model such fine grained control and specification over concerns like input validation should be understood, unless that validation is unique or specific to a business need.
Simply put in any case, in the example where you wish to verify a duplicate record is not entered into a database is far too low level for a user story, and one would argue is a waste of valuable time to elevate to the importance of an Acceptance Test. Quality assurance or the tester for that feature should be able to verify that the high level requirement has been met with no obvious defects.
Your tests need to be split up:
Automated Unit Tests
For your lowest level tests, typically written and performed by the developer to verify all of the functionality of a specific component or application layer apart, independent of other areas of the application, and reproducible to run multiple times.
Integration Tests
These tests like unit tests can verify situations like creating a new Person record, across all system layers, verifying the integration of all application dependencies while verifying that creation a new Person record, or preventing a duplicate Person record from being created is occurring correctly.
The Case for Integration Tests
One of the most valuable aspects of these kinds of tests is that their are various strategies to not only Automate these tests, one can also use database transactions to make them run in parallel and not conflict, and also this database transaction can be Rolled Back when the test is over reverting your database back to a clean slate and making them reproducible.
I gather that based on your question that most of your "Acceptance Tests" aren't terribly interesting and would be better automated. This isn't to say that Acceptance testing shouldn't occur, but it should be done on a much higher level to where these issues you bring up no longer matter.
Best Answer
Besides the fact that this is an integration test as opposed to a unit test, the operations you describe typically go in
Setup
and/orTeardown
methods. Frameworks like nUnit allow one to decorate class methods with these attributes to indicate whether the method is a setup method or teardown method.Then your tests should become cleaner and smaller as the setup and cleanup is done outside of the test itself.
Most likely multiple tests can re-use the same data so that is a plus as well as opposed to insert/remove on every test. Going back to nUnit, the
FixtureSetup
andFixtureTeardown
attributes help to setup data for multiple tests at once.I would use a test framework over a try/catch as many of these testing features are built into the framework itself. xUnit, nUnit, even the Microsoft built in testing framework are all solid choices and will help with the setup and clean up of database records in a consistent fashion.