Recipe 7.11. Speeding Up Tests with Transactional Fixtures


Problem

You have some tests that are taking too long to run. You suspect that the problem is in the setup and teardown for each test, and want to minimize this overhead.

Solution

Setting the following two configuration options in your test_helper.rb can significantly improve your tests' running performance. Note that the first option, self.use_transactional_fixtures, works only if you are using a database that supports transactions.

test/test_helper.rb:

ENV["RAILS_ENV"] = "test" require File.expand_path(File.dirname(__FILE__) + "/../config/environment") require 'test_help' class Test::Unit::TestCase   self.use_transactional_fixtures = true   self.use_instantiated_fixtures  = false end

Discussion

In Rails, when you run unit tests that use test fixtures Rails makes sure that each test method operates on a fresh set of test data. The way in which Rails resets your test data is configurable. Prior to Rails 1.0, test data was torn down and re-initialized using SQL delete and multiple insert statements for each test method. Making all of these SQL calls slowed down the running of tests. So to help speed things up, transactional fixtures were added to cut down on the number of SQL statements that must execute for each test method. Transactional fixtures work by surrounding the code of each test method with begin and rollback SQL statements. This way, all changes that a test case makes to the database are simply rolled back as a single transaction.

MySQL's default storage engine is MyISAM, which does not support transactions. If you're using MySQL and would like to take advantage of transactional fixtures, you must use the InnoDB storage engine. Do this by specifying the engine type with the following Active Record migration statement:

create_table :movies, :options => 'TYPE=InnoDB' do |t|   #... end

This is equivalent to:

CREATE TABLE movies (   id int(11) DEFAULT NULL auto_increment PRIMARY KEY ) ENGINE=InnoDB

You can also update existing tables with pure SQL:

alter table movies type=InnoDB;

The solution's second configuration option, self.use_instantiated_fixtures, tells Rails not to instantiate fixtures or create instance variable for each test fixture prior to each test method. This saves a significant amount of instantiation, especially when many of your test methods don't use these variables. This means that instead of referencing an employee test fixture with @employee_with_pto, you use the fixture accessor method, such as: employees(:employee_with_pto).

Using fixture accessor methods accesses fixture data as it is needed. Calls to these accessor methods instantiates each fixture and caches the results, so subsequent calls incur no extra overhead.

Another way to deal with the loading of fixture data into your test database is to pre-populate it with:

$ rake db:fixtures:load             

With your fixtures preloaded and self.use_transactional_fixtures set to TRue, you can omit all calls to fixtures in your tests.

See Also

  • Section 7.4"




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net