I have (belatedly) started testing my Rails app (a shopping website) with RSpec/capybara, using database cleaner to clear the database and Factory Girl to generate new objects for every test (like most people do). This works fine, and I do think it is a good idea to clear the data between tests.
However, it can get slow and (as far as I can figure out) a bit tedious to generate multiple instances of the same object. There are some objects that are always the same in my database, or that I will always generate an identical copy of for testing. For example, my Package model, which defines the pricing and feature limits for a subscription package. It will probably never change.
Is there a way, with this configuration (please comment and specify if you require more info), to put certain instances of objects in the test database and exclude them from Database Cleaner, or any other way to keep permanent copies of specific objects in your test database?
This is mainly to increase testing speed.
If you have database objects that your application never changes, and that are the same in your production and development databases as well as in your test databases, the right thing to do is to make them seeds. Create them in db/seeds.rb. More on seeds here: http://guides.rubyonrails.org/active_record_migrations.html#migrations-and-seed-data
If the objects that you're talking about only belong in your test database, you could make them Rails fixtures. More on fixtures here: http://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures (Beware, though, that fixtures are usually a bad idea, because they make your tests harder to read and encourage you to write tests around existing fixtures, which leads to an entangled mess. Test clarity and robustness is more important than speed.)
If you're using Database Cleaner's truncation or deletion strategy (probably because you're using a Javascript-capable driver with Capybara), and you've used either of the foregoing methods to leave data in your test database between tests, you can tell Database Cleaner to not empty specific tables:
DatabaseCleaner.strategy = :truncation, {:only => %w[widgets dogs some_other_table]}
or
DatabaseCleaner.strategy = :truncation, {:except => %w[widgets]}
(Source: https://github.com/bmabey/database_cleaner#how-to-use) I don't know of a way to tell Database Cleaner to delete some instances of a given class and not others, however.
Related
TLDR: What is the best approach to seed global data with FactoryBot?
Currently, we have a fairly large application and using FactoryBot for most tests. However, we are still using fixtures to seed some default data in a database, for CRUD tests that need to talk to the DB, and can't be "stubbed".
Using fixtures to seed data, lets us shave off 50% of the time during setup/teardown for things that are common for almost all CRUD tests.
Also for some business rules validations, we need to use actual data, not 'faked' from Faker.
Examples of the seed model/fixtures:
ShippingMethod (FedEx, UPS, USPS, etc. )
User (user record is used in almost every other model)
Warehouse (we only have 2)
Setting ( some global app settings)
Actual Question
What is the best practice to use inside FactoryBot to move away from using fixtures completely, so that we maintain test data in one spot, or this a good use-case for fixtures to be used with factories?
Keep in mind that since tests are run in parallel, seed data needs to be available to all tests throughout the run of the test suite.
Thank you for any suggestions.
I would like to have a varying number of records saved to the database for different tests. The purpose of one test would be to see a method's behavior with only one record in the database while another might be to see how the method behaves with ten records. I understand Fixtures are the preferred way to load data into the testing database, but as far as I know, Fixtures are static and can't be molded for a specific test. I am therefore, using CRUD operations in some of my tests. Most of these tests begin by deleting all the records from the table being tested then adding records that suit the specific test.
The tests that rely on CRUD operations and do not use Fixture data I have placed at the end of my tests so they don't interfere with the tests that use Fixtures. I am new to testing in Rails and would like to know if bypassing Fixtures and using CRUD operations in unit tests is a good/bad practice?
You'll see all kinds of answers, from using fixtures to factories to POROs ("Plain Old Ruby Objects"), but I think in general you want to minimize or eliminate database interaction as much as possible when working at the unit testing level, while leaving that more for integration or acceptance tests.
While unit testing, it might be helpful to think about inputs and outputs, and how/what messages are sent to and from each "unit" under test.
There are plenty of books, articles, and posts out there on this topic, but in the end I'd recommend a balance between practicality and the ideal worldview some of these present.
You might find this interesting:
https://www.youtube.com/watch?v=qPfQM4w4I04
I have (belatedly) started testing my Rails app (a shopping website) with RSpec/capybara, using database cleaner to clear the database and Factory Girl to generate new objects for every test (like most people do). This works fine, and I do think it is a good idea to clear the data between tests.
However, it can get slow and (as far as I can figure out) a bit tedious to generate multiple instances of the same object. There are some objects that are always the same in my database, or that I will always generate an identical copy of for testing. For example, my Package model, which defines the pricing and feature limits for a subscription package. It will probably never change.
Is there a way, with this configuration (please comment and specify if you require more info), to put certain instances of objects in the test database and exclude them from Database Cleaner, or any other way to keep permanent copies of specific objects in your test database?
This is mainly to increase testing speed.
If you have database objects that your application never changes, and that are the same in your production and development databases as well as in your test databases, the right thing to do is to make them seeds. Create them in db/seeds.rb. More on seeds here: http://guides.rubyonrails.org/active_record_migrations.html#migrations-and-seed-data
If the objects that you're talking about only belong in your test database, you could make them Rails fixtures. More on fixtures here: http://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures (Beware, though, that fixtures are usually a bad idea, because they make your tests harder to read and encourage you to write tests around existing fixtures, which leads to an entangled mess. Test clarity and robustness is more important than speed.)
If you're using Database Cleaner's truncation or deletion strategy (probably because you're using a Javascript-capable driver with Capybara), and you've used either of the foregoing methods to leave data in your test database between tests, you can tell Database Cleaner to not empty specific tables:
DatabaseCleaner.strategy = :truncation, {:only => %w[widgets dogs some_other_table]}
or
DatabaseCleaner.strategy = :truncation, {:except => %w[widgets]}
(Source: https://github.com/bmabey/database_cleaner#how-to-use) I don't know of a way to tell Database Cleaner to delete some instances of a given class and not others, however.
I'm new to testing in rails and I don't understand why or when I should use fixtures (or factory) rather than just seeding my test db and querying it to run the tests?
In many cases, it should be faster and easier to have the same data in dev and test env.
For example, if I want to test an index page, should I create 100 records via a factory or should I seed the db with 100 records?
I someone could clarify this, it would be great.
Thanks!
This is actually a deeper question of how to test efficiently, and you will find a lot of different opinions.
The reason to avoid a database in your unit tests is merely speed. Database operations are slow. It might not seem slow with one test, but if you have continuous integration going (as you should) or when you made a quick change and just want to see what happens, those delays add up. So prefer mocks to truly unit test code.
Then your own integration tests should hit an in-memory database rather than your real database--for the same reason, speed. These will be slower than your mocked tests, but still faster than hitting the real database. When you're developing, the build-test-deploy cycle needs to be as fast as possible. Note that some people call these unit tests as well. I wouldn't, but I guess it is just semantics.
These first two kinds of tests are by developers for developers.
Then the testers will hit the real database, which will be populated with test data defined by the testers and subject-matter experts. There are lots of clever ways to speed this up as well, but this will be the place where they test the integration of your code with the production-like database. If all your in-memory database tests passed and something goes wrong here, then you know it has to do with something like database configuration, vendor-specific SQL, etc. rather than something fundamentally bad. You will also get your first taste of what the performance is like.
Note that everything I've said here is a matter of debate. But hopefully it clarifies what you should consider about when to do certain things and why.
I'm maintaining a Rails 3.1 app. App db has more than 50+ tables and maybe 30 of those need seed data for the app to function correctly.
App has plenty of statistical data (as a seed data) and some tables contain more than 150 000 records. I have been testing using fixtures (actually using rake tasks to create fixture files from dev-db). Because of huge fixture files, testing has become slower and slower. We're talking about 20+ minutes to run the whole test suite.
At the time I started making tests, fixtures were way to go. Currently I'm not so sure anymore. I keep reading about tools like factory_girl, capybara, rspec and spork. I've done few tests with those and they seem nice and fun to use.
Basically I'd like to know how would you test this kind of setup?
Fixtures are way too slow. Thanks for the help!
Well, with application as huge as yours, the test suite should be running very long also. I think the greatest improvement here would be using less testing data in the database. You can test associations or whatever it is you're doing db-related, but when you're testing model functionality for example, set up mock expectations on #save method and verify that your code changed #attributes of the model. I think that testing everything against the database is redundant. You don't have to include rails stack as your testing target (which you do when you save to the database), as it's very thoroughly tested already.