Blankman
Blankman

Reputation: 267000

Unit Testing in web applications that use databases

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?

Upvotes: 13

Views: 1464

Answers (8)

dcousineau
dcousineau

Reputation: 2212

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.

Upvotes: 1

Jon Wolski
Jon Wolski

Reputation:

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.

Upvotes: 2

John Goering
John Goering

Reputation: 39030

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

Upvotes: 0

Jeffrey Fredrick
Jeffrey Fredrick

Reputation: 4503

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.

Upvotes: 0

Ulf Lindback
Ulf Lindback

Reputation: 14170

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.

Upvotes: 5

guerda
guerda

Reputation: 24049

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

Upvotes: 7

matt b
matt b

Reputation: 139931

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.

Upvotes: 6

Jason Baker
Jason Baker

Reputation: 198577

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.

Upvotes: 1

Related Questions