This might be a basic misunderstanding on my part. I have a bunch of logic in my app which collects sets of data from several tables and combines them into memory structures. I want to write tests for that logic.
It seems to me that fixtures, factory girl and similar tools build in-memory model instances. If I do activerecord calls, like Model.find(foo: 12) won't those apply only against records that were saved?
In most cases, I agree with #mrbrdo's opinion: prefer rpsec's stub method. but as a rails programmer, I think you have to know both the "fixture" and the "stub".
Fixtures, whatever the yaml file or the factory girl data , will both save into database. see your config/database.yml file for the location where they are. Actually this is useful when you want to make sure that there is ALWAYS some data in the DB during your test, such as an "admin user" with a fixed ID.
Stubs, it's faster than fixture since it won't be saved into DB, and maybe very useful when you want to perform a test that can't implemented by "Fixtures".
So, I suggest that you try both of them in your real coding life, and choose either of them according to the real context.
What you are saying is not true, fixtures or factory girl will use the database. I would avoid fixtures, though, people don't usually use them nowadays.
The proper way to really write your tests would be to stub out activerecord calls, though, because this will make your tests a lot faster. What you want to test is combining data into your structures, not pulling data out of the database - that part is already tested in activerecord's tests.
So stub out the finders like this (if you are using rspec):
Model.should_receive(:find).with(foo: 12) do
Model.new(foo: 12, other_attribute: true)
end
So when the method you are testing calls Model.find(foo: 12) it will get
Model.new(foo: 12, other_attribute: true)
This is much faster than actually creating it in the database and then pulling it out, and there is no point in doing this for what you are testing - it's not important. You can also stub save on the instance and so on depending on what your method is doing. Keep in mind, retrieving data from DB and saving to DB is all already tested in activerecord's tests, there is no point for you to re-do these tests - just focus on your specific logic.
FactoryGirl supports several build strategies, including one where records are saved to the database.
It's straightforward: FactoryGirl.create(:foo) will create a foo and save it to the database, whereas FactoryGirl.build(:foo) will only create the in-memory version of that object.
More information is available about build strategies here: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md , in the "Using factories" section.
Related
In my Rails 4 project I've got a model, EventGenerator with an instance method generate (which creates some records in the database), and a class method generate_all which is intended to be called by a Rake task and looks something like this:
def self.generate_all
all.each(&:generate)
end
I can think of several approaches to testing this (I'm using RSpec 3 and Fabrication):
Don't bother - it's too simple to require tests. DHH says "Don't aim for 100% coverage". On the other hand, this is going to be called by a rake task, so won't be regularly exercised: I feel like that's a good reason to have tests.
Create a couple of EventGenerator instances in the database and use any_instance.should_receive(:generate) as the assertion - but RSpec 3 now recommends against this and requires a fudge to make it work. This is a personal 'showcase project' so if possible I'd like everything to be best-practice. Also (DHH aside) shouldn't it still be possible to create fast model specs which don't touch the database?
Like 2, but stub out EventGenerator.all to return some instances without touching the database. But stubbing the class under test is bad, right? And fragile.
Don't worry about unit testing it and instead cover it with an integration test: create a couple of generators in the database, run the method, check what gets changed/created in the database as a result.
Change the implementation: pass in an array of instances. But that's just pushing the problem back by a layer, and a test-driven change which I can't see benefitting the design.
Since I can't really think of a downside for option 4, perhaps that's the answer, but I feel like I need some science to back that up.
I would actually not bother to test it (so your 1.) as the method is really trivial.
If you would like to have it under test coverage though I'd suggest you to use your 3. My reasons are as follows:
Your test for .generate_all just needs to assert that the method #generate gets call on every instance returned by .all. It this context the actual implementation of .all is irrelevant and can be stubbed.
Your tests for #generate should assert that the method does the right thing. If these tests assert the proper functioning of this method, there's no need for the tests for .generate_all to duplicate any assertion.
Testing the proper functioning of #generate in the tests for .generate_all leads to unnecessary dependencies between the tests.
Is there a smart way to test a scopes without hitting the db?
I am asking because I am dealing with scopes on multi-level of embedded documents, and to actually create the objects in db would require a long chain of objects to be created first.
This seems inefficient performance wise, and really cumbersome to write to test a single scope.
I'm not sure of the implications of your multi-level of embedded documents, but you can assert that a scope is a Mongoid::Criteria, and further assert that it is a criteria with certain properties.
RSpec examples:
expect(Charge.successful).to be_a Mongoid::Criteria
expect(Charge.successful).to eq Mongoid::Criteria.new(Charge).where(state: 'successful')
Note that this isn't testing behaviour, which would be best practice.
A better approach might simply be to bite the bullet and use something like FactoryGirl to create your objects and test behaviour. Presumably to fully acceptance test your system you'll need these objects anyway.
If you'd like to avoid 'hitting the DB', you could use EmbeddedMongo. This would probably speed up your tests and mean you wouldn't need MongoDB on your CI server.
In my Rails app development, When I run my Rspec test, I need to truncate all tables in my test database in after(:all) .
(That's to clean up all data in every table in test db)
To approach this, I am thinking to first get all ActiveRecord models which represent the tables in test db, then for each model, I use delete_all method to clean up each table. Thant's something like:
ALL_ACTIVE_RECORD_MODELS.each do |model|
model.delete_all
end
I have two questions to ask regards to this:
1. How to get all active record models in Rails in my rspec code?
2. Am I using a acceptable way to truncate all tables in my test DB or not? If not, what is the alternative way?
There is a gem to do exactly this task called database_cleaner: https://github.com/bmabey/database_cleaner.
It will make sure that everything is removed from your database however its default strategy is not to delete content but to use transactions and simply roll back the changes after each test.
Be warned that this can occasionally lead to a gotcha when testing behaviour that's intended to be transactional as you won't see your transaction execute. You can fix this by adding self.use_transactional_fixtures = false before any set of tests that you don't want to use transactions. Remember to clear your data away again afterward though.
I am using Ruby on Rails 3.0.9, RSpect 2 and FactoryGirl. I would like to know how I should proceed in my case relating to factory usages for testing purposes.
I have seeded the database (by using the /db/seed.rb file) with some data that is necessary in order to initialize a make my application to work. Now I am in trouble because I implemented\created some factories and when I instantiate them (eg: Factory(:user), Factory(:user_authorization), ...) those are "combined" with the seeded data present in the database.
So, the question is: should I use factories exclusively (that is, to use only factory data by not considering the test database data) or can I use those in "combination" with the seeded data in the database? That is, should I consider also database seeded data or I should implement\emulate all seeded data with factories?
In my projects I use seeding for the data my application really needs to get started. E.g. the first user (admin), domain tables, ... For these I do not use factories.
Secondly I use factories during testing to simulate any data that should be present.
In my opinion those co-exist perfectly. I use the factories when the contents of the data is actually not that important (there has to be some user, some post, some comment ...), but with the seeding the actual data in the database is very important, and if I would be using factories for that, I would be redefining every attribute anyway, so for me there would be no benefit of using a factory.
I hope this helps.
You can use factory_girl within in seed.rb. This post explains it a little more. http://xtargets.com/2011/02/06/seeding-your-rails-db-with-factory-girl/
In Rails, fixtures records seem to be inserted & deleted in isolation for a given model. I'd like to test having many objects/rows in one transaction, eg. to check uniqueness. How to do it?
Fixtures are not validated. When you set them up they can be totally wrong and Rails won't complain until something blows up. It's a good idea to make sure your initial test DB (that is seeded with your fixtures) is in a valid state before tests are run.
For checking things like uniqueness, I would create the records on the fly and not rely on fixtures. Either create them right in your test case, or use something like FactoryGirl (which by the way, is a great way to clean up your tests and stop using fixtures completely).
Are you saying you want to build a test to check the rails "validates_uniqueness_of" operator or that you want to test the logic of your own unique record? In the first case, I wouldn't bother, the Rails tests cover that. In the second case, I would create a test that creates a record that is the same as one in the fixtures.
In the broader sense of putting multiple saves into a single transaction, you can create your objects and then:
MyModel.transaction do
model1.save
model2.save
end
but I don't think this is the way to accomplish either of the things it seems that you want to do.