Unit tests that hit the database are generally slower than those that don’t by an order of magnitude. Not hitting the database in specs often makes your code better as well. You are forced to stub out collaborators that would hit the database and stubbing things out forces you to think more about the actual interface. Suddenly things like
become red flags when called from a controller. They make you do things like
stub_chain which is usually just a tool to enable Law of Demeter violations. You’d likely be better served by a more specific scope or, even better, a query object.
Yes, you could use a sqlite in memory database to potentially speed things up but you’re still going to be much slower than a test that simply stubs the database access.
To this end, we’ve decided to prevent access to the database from specs that don’t explictly request or “naturally” need it. A test will fail if it hits the database.
Why not just be diligent about it? A couple reasons. First and foremost, we like to encourage the Pit of Success. The more we can do to make it difficult to do bad things the better. Also, Rails makes it pretty hard to know when you’re hitting the database.
To make sure a test fails when it hits the database, we freedom patch the
Mysql2Adapter and add some RSpec configuration. This could likely be ported to minitest or TestUnit or whatever other framework you fancy.
A few things to note:
- Adding this to an existing project will take some work, but it will likely expose plenty of things to talk about as it did for us.
- We only prevent SELECT, UPDATE, INSERT and DELETE. Things like
stub_modelneed to hit the database to get column information. We don’t want to stop this.
- Request specs, model specs and any spec with
:db => truewill be allowed to hit the database. You can customize this in the
config.before :eachblock above.
You also may want to check out nulldb which will let you run tests against a fake database. This can speed things up when you want to test things like observers or