Should rails units tests hit database or not?
I have been writing tests for my rails application. I use TestUnit for unit as well as functional tests. And I use cucumber for GUI testing as well.
But I find that http://www.dcmanges.com/blog/rails-unit-record-test-without-the-database says that it is better for unit tests not to hit the database.
I do agree that hitting database takes considerable time. I already use spork to reduce environment loading.
What is the best practice when it comes to testin开发者_运维知识库g a rails app?
This is one of the cases where you shouldn't get too hung up on names. Rails refers to tests that exercise the ActiveRecord models as "unit" tests, largely because they're the lowest level tests directly supported by the framework. According to testing lore, to which I'll admit I tend to cleave fairly consistently, unit tests should have no dependencies external to the unit under test; this means things like persistence mechanisms, i.e. the database.
That said, testing ActiveRecord models of any complexity without the database will quickly make you want to eat your hands. Rails assumes that you use the database for tests; that's simply the way the framework is written. You can try to stub everything out, but you'll eventually fail since you'll end up messing around in the internals of ActiveRecord associations (not for the faint of heart). You can try to extract all of the non-persistence-related code into separate modules that test without the database, but you'll be creating quite a lot of unnecessary complexity.
Don't try to fight the framework. ActiveRecord and ActionController have very specific patterns of expected use, both in production and in tests. Rails, after all, is all about convention. If you follow the pattern of use you will do much less work, and become much less frustrated, than if you fight the conventions in order to stick with unit testing ideals. If it makes you feel better, consider your ActiveRecord tests to be "model" tests, and your functional tests to be "controller" tests (this is the terminology that rspec uses, incidentally).
All that said, you shouldn't access the database unnecessarily in your model tests (or specs). Some validations, such as uniqueness, require the database, but most do not. Many actions related to create/save callbacks and associations require the database, but others do not. Be aware of what the actions you're invoking are doing.
Unit tests should not hit the database. They should test that the methods run as expected if the right data is available (regardless of where that data comes from). Therefore you should be stubbing/mocking as necessary to ensure that each unit test is isolated.
However integration tests (as you might write with Cucumber, for example) should tie the entire MVC stack together, and there's no reason for those not to hit the database
The argument that it's the "Rails way" to test models w/ "unit tests that hit the DB" is the same thing as saying "well everybody jumps off a bridge, maybe I should?".
Every Rails convention that you follow should be seriously analyzed, because in hindsight "monolothic Rails apps", if they followed this advice, they'd have 100+ models with integration tests. Even if you tagged the specs with slow and fast, you have no unit tests around the models in the "fast" category (unless you wrote them as well, in that case maybe you're testing TOO much).
Side affect: a slow unit testing suite that takes an hour. And lets say you stick every method in your models instead of abstracting it out to different classes, you're in a more world of hurt, because you need to write integration tests for them as well.
I know that no matter what you do, integration tests will still be slow, but the only way you can fight the slowness is by writing them smartly.
I'm trying out writing 'request specs' that hit the DB from the HTTP request down. And use them as my "functional tests". Every class that I write, whether it be a service, model, or what-have-you, has unit tests around their public methods (that stub out the DB obviously).
Use a fast in-memory database for unit testing. If you want to test "methods run as expected if the right data is available", it is better to use a data set that closely resembles your real data set. An in-memory database can contain very small or fairly large data sets, depending on your needs. HSQLDB is supported by ruby-on-rails and is commonly used for unit testing.
精彩评论