after(:each) vs after(:all) in Rails rspec - ruby-on-rails

In rails rspec, I am writing test cases that do this:
after(:each) do
DatabaseCleaner.clean_with(:truncation)
end
I need the database to be cleaned after each test is run. But will this affect the performance of my tests and make them slower?

The "truncation" method has an impact based on the number of tables, because it will execute one query per table to truncate. You can use the "transaction" method instead, which will do a transaction-based rollback instead, which will drastically decrease the amount of resources used to roll back to a clean database state.
If you use ActiveRecord and want to have a clear picture of what happens, you can do something like this in your test:
before do
ActiveRecord::Base.logger = Logger.new(STDOUT)
end

It will have impact on performance depending on the number of tests on which the after hook will be run.
Also, I think it is best to use before instead of after. Depending on the test case, the state of the database is a precondition; the test case requires that precondition. And you should ensure that precondition with the before hook instead of relying on after hooks defined somewhere else.

Related

before(:all) except one - is it possible?

I use before(:all) in my tests for database initialization, which causes no problems for my test cases... except one. I'd like to run this special case before or after others, as I want to initialize database for it separately. Is there some way to do this? I'd like to avoid replacing before(:all) with before(:each), because I'd like to keep my tests fast. Is it possible?
before(:all) except one - is it possible?
Probally not. before(:all) runs before all the examples in that scope and there is no way to hoist examples above it.
I think a better idea is to work around the problem and just create different contexts:
RSpec.describe Thing do
context "with :all" do
before(:all) do
# ...
end
# ...
end
context "without :all" do
# ...
end
end
Use shared contexts or the outer scope if you need to share setup steps or variables. Use shared examples if you want to run the same examples in different contexts.
I'd like to run this special case before or after others, as I want to
initialize database for it separately. Is there some way to do this?
You can use the --order defined option when running rspec to run the tests sequentially. However this sets you up for test ordering issues than can mask critical bugs in the application. Fast test are worthless if they don't catch bugs.
You can also use :order => :defined metadata to set the order per context. And for the reason in the beginning of this question this is a fools errand.

Ruby on Rails2.3.8: Unit Testing: Rails/Ruby has setup to run before each test. What about a method that runs before all tests?

I'd like to init the data base once everytime i run tests, rather than every test.
I know with Rspec there is before(:all), but I haven't been able to get that working. I was wondering if rails had something similar.
Firstly: there used to be a before(:all) equivalent in Test::Unit but it was removed (don't know why).
Secondly: there are very good reasons not to do what you are trying to do - tests are meant to be run independently of one another, not rely on state that's in the db. This way you can guarantee that it's testing exactly what you are expecting it to test.
If you have one test that changes the state of the db, and you move it and it runs after another test which expects it to be another state - you run into problems. Thus, all test must be independent.
Thus: the db is rolled back to its pristine state and re-seeded every time.
If you really want some state that the db is always in - then set it up in the fixtures... and just realise that the db will be re-loaded for each test.
If you are having trouble with load-times... then consider figuring out some other way around the problem - eg don't use huge numbers of fixtures, instead use Factories to only create the data that you need for each individual test.
If there's some other reason... let us know - we may have a solution for it.
Edit: if you really need it, I actually wrote a monkey patch for this a long while back:
"faking startup and shutdown"
All things to run before everything just go in the top of the class
require 'test_helper'
class ObjectTest < ActiveSupport::TestCase
call_rake("db:bootstrap RAILS_ENV=test")
#set up our user for doing all our tests (this person is very busy)
#user = Factory(:user)
#account = Factory(:account)
#user.account = #account
#user.save
# make sure our user and account got created
puts "||||||||||||||||||||||||||||||||||||||||||||||"
puts "| propsal_test.rb"
puts "| #{#user.name}"
puts "| #{#user.account.name}"
puts "||||||||||||||||||||||||||||||||||||||||||||||"

Factory Girl & Rails: can we turn off the database transactions that wrap each test?

I've got this issue with database locking when I'm testing some threading features I've got in my application--the database locks on one thread and then all the other threads deadlock on that. As there's no explicit transaction in my code I can't just guard and release manually.
I read somewhere that fixtures in TestCase leverage implicit (implicit to MY code) transactions to clean out the database between tests, but I can't find anywhere if this is true for Factory Girl as well.
Does anyone know if it is true and if so, is there a way to turn it off for specific tests, but not all of the tests?
thanks in advance!
FactoryGirl just takes your model, sets the attributes, and calls #save on that model. I think you just need to change a setting. With RSpec, you should have a line in your spec_helper.rb file:
config.use_transactional_fixtures = true
This config is sent to the Rails testing config. This is then used in the #setup_fixtures method.
The documentation on Rails transactional fixtures
HTH

How to skip certain tests with Test::Unit

In one of my projects I need to collaborate with several backend systems. Some of them somewhat lacks in documentation, and partly therefore I have some test code that interact with some test servers just to see everything works as expected. However, accessing these servers is quite slow, and therefore I do not want to run these tests every time I run my test suite.
My question is how to deal with a situation where you want to skip certain tests. Currently I use an environment variable 'BACKEND_TEST' and a conditional statement which checks if the variable is set for each test I would like to skip. But sometimes I would like to skip all tests in a test file without having to add an extra row to the beginning of each test.
The tests which have to interact with the test servers are not many, as I use flexmock in other situations. However, you can't mock yourself away from reality.
As you can see from this question's title, I'm using Test::Unit. Additionally, if it makes any difference, the project is a Rails project.
The features referred to in the previous answer include the omit() method and omit_if()
def test_omission
omit('Reason')
# Not reached here
end
And
def test_omission
omit_if("".empty?)
# Not reached here
end
From: http://test-unit.rubyforge.org/test-unit/en/Test/Unit/TestCaseOmissionSupport.html#omit-instance_method
New Features Of Test Unit 2.x suggests that test-unit 2.x (the gem version, not the ruby 1.8 standard library) allows you to omit tests.
I was confused by the following, which still raises an error to the console:
def test_omission
omit('Reason')
# Not reached here
end
You can avoid that by wrapping the code to skip in a block passed to omit:
def test_omission
omit 'Reason' do
# Not reached here
end
end
That actually skips the test as expected, and outputs "Omission: Test Reason" to the console. It's unfortunate that you have to indent existing code to make this work, and I'd be happy to learn of a better way to do it, but this works.

Handling Non-User data in Rails tests

We have a ton of non-user data: 500 product types that haven't changed in 20 years, 200GB of geospatial data that never changes (Queens is always 40.73N/73.82W)... etc. It's the same in Development and Production. It should be the same in Test, but the during testing Rails empties all of the test tables, and it takes a ton of time to re-populate during testing.
What is the Rails way to partition this non-user data so that it doesn't have to be repopulated in Test?
The documentation for this is found in the Fixtures class. (Search for "Transactional fixtures" on that page.)
The example they give starts out like this:
class FooTest < ActiveSupport::TestCase
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
...
One of the projects I work on uses a test database with zero fixtures, so we just define this globally in test_helper.rb.
class ActiveSupport::TestCase
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
end
jdl shows you the settings to use for enabling and disabling transactional_fixtures, you should be able to set:
# Use self in ActiveSupport::TestCase if you don't have a config block
# in test/test_helper.rb
#
config.use_transactional_fixtures = false
And stop Rails from trying to load fixtures before each run of your tests. The downside is you can't assume all your fixtures are loaded into the DB.
Real Answer
You have too much data in your fixtures. The Railsy thing to do it load only the necessary data in test fixtures; You do not need 200G of geospatial data (what freaking dataset is that anyway? that sounds way too big), and you probably don't need all 500 products.
Tests are for your code, so only include the few geospatial points you need for a test, or only include a few products with unique properties. Keep the DB light enough to be loaded quickly.
Your test fixtures should be separate from you application bootstrapping data, take advantage of that.
Real Real Answer
Don't touch the database in your tests (or, touch it very little). The database is slow and fixtures are difficult or impossible to maintain well. Instead, try using a stubbing framework like Mocha or flexmock. It's faster, and makes your tests read in a clearer manner. Tests are for code, you can stub the database out of the situation and trust that ActiveRecord works (because...it's tested too! :-).
If you do choose to stick with fixtures, I recommend using factory_girl instead of the built in Rails fixtures. You'll have a much better starting point.

Resources