views:

527

answers:

8

Hi,

I am building a web application that uses the database for Users, Security/roles, and to store content.

It seems a little daunting to me to begin on the road of unit testing because I have to make sure my database has been initialized properly for my tests to run.

What are common practices to help in this regard?

i.e. while developing/testing, I might delete a user, but for my test to pass that user has to be in the database, along with his profile, security settings etc.

I know I can create a setup script, something to recreat the databas etc.

I don't want to end up spending my entire time maintaining my tests and ensuring my database is in sych

Or is that the cost of Unit Testing/TDD?

+1  A: 

Michael Feathers argues that tests that communicate with databases aren't unit tests by definition. The main reason for this is the point you bring up: unit tests should be simple and easy to run.

This isn't to say that you shouldn't test database code. But you don't want to consider them unit tests. Thus, if you do any database testing, you want to separate the tests from the rest of your unit tests.

Jason Baker
Most of my code, besides very utils, use database. So if I know that if I have 5 objects in db, I should have as a result number 3. And in Unit Test I can check for "3". But what with mocking, in such case?
dynback.com
+6  A: 

It's not a unit test if you are testing more than one unit.

Usually you'll have one component (your page, or the business layer) talking to a data layer object that is responsible for actually connecting and querying the database. My recommendation is to develop a unit test for the first component, using dependency injection to pass in a mock version of the DataLayer (which acts on hardcoded data, or a List you pass in, etc). This way you are testing your higher level code in isolation from the other components.

Then you are free to develop other unit tests (and integration tests) for the data layer to ensure that it is handling it's job (writing to the database) correctly.

matt b
My problem with that is that you then spend as much time writing and debugging and maintaining your mock datalayer as you do maintaining your real datalayer, and with no payback.
Paul Tomblin
I disagree, and I haven't had that problem in practice. I guess it depends on the size of your data layer. I like to use a DAO approach, where I have one DAO per domain object; the operations then are pretty small (get, add, delete, etc). Most of the time I can implement the Mock class in a few...
matt b
.. minutes, just by creating a class that operations off of a List that the caller can pass in via the constructor. But you are correct that it can feel like you are writing code just to support a test - but I think that's better than having to manage a test database, make sure data is present, etc
matt b
Or you can use Easymock, as I do now. Man my comments from 4 months ago seem so old!
matt b
+7  A: 

The solution is Mocking. Mocks "replace" the connection. The unit under test will "connect" to the Mock and executes its statement. The Mock returns normal resultsets o.s.e.

After the test, the mock can give you a list of all methods, that were called by the unit under test. Easymock.org

As the other said: DB connection aren't a unit test. So drop it and do it local with Mocking objects

furtelwart
+5  A: 

We use an in-memory database (hsqldb) for our unit tests. In the setup we populate it with test data and then before each test case we start a transaction and after each test case we roll it the transaction back. This means each testcase has a clean start of the db.

Ulf Lindback
A: 

The cost of unit testing/TDD is that you have to alter your design so that you have a clean separation between the database and the domain layer, so that you can create a fake that will allow you to create tests that don't hit the database.

But that cleaner design is just the beginning of the cost. After that you have to create tests that both help you make the code work right the first time and alert you when anyone breaks something that used to work.

And after you have a good fundamental design with tests that protect your existing functionality, you'll find yourself cleaning up code to make it easier to work with, with confidence you aren't breaking things along the way.

And so on and so on... The costs of unit testing/TDD just keep piling up over time.

Jeffrey Fredrick
A: 

For Java, you may also want to look into dbunit. http://www.dbunit.org/

Epaga
+2  A: 

It sounds like you actually want functional/integration testing. For Web applications I recommend you look into Selenium or Canoo WebTest.

These are also great for automating tasks you do on the Web. I have a set-up suite and a tear-down suite that create business entities and testing users through the admin interface as well as tests for the customer-facing site.

+1  A: 

Since I used Doctrine for my PHP database work, and since Doctrine has a query abstraction layer (called DQL), I can swap out back ends without having to worry too much about compatibility issues. So in this case for my unit tests I would just at the beginning of my tests load the schema and fixtures into a SQLlite db, test my models, and discard the SQLlite db at the end of testing.

This way I've tested my models and data access to make sure their queries are formed correctly.

Now testing the specific database instance to make sure the current schema is correct is a different story and IMHO probably doesn't belong in your Unit Tests, so much as it belongs in your deployment task list.

dcousineau