4 require 'active_support/test_case'
6 if RUBY_VERSION < '1.9'
9 def keys
; map
{ |k
, v
| k
} end
10 def values
; map
{ |k
, v
| v
} end
15 if defined? ActiveRecord
16 class FixtureClassNotFound
< ActiveRecord
::ActiveRecordError #:nodoc:
19 class FixtureClassNotFound
< StandardError
#:nodoc:
23 # Fixtures are a way of organizing data that you want to test against; in short, sample data. They come in 3 flavors:
27 # 3. Single-file fixtures
31 # This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
32 # in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
34 # Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed in the directory appointed
35 # by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
36 # put your files in <tt><your-rails-app>/test/fixtures/</tt>). The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
37 # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a YAML fixture file looks like this:
42 # url: http://www.rubyonrails.org
47 # url: http://www.google.com
49 # This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
50 # indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
53 # Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type. See http://yaml.org/type/omap.html
54 # for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
55 # This is commonly needed for tree structures. Example:
69 # Fixtures can also be kept in the Comma Separated Value format. Akin to YAML fixtures, CSV fixtures are stored
70 # in a single file, but instead end with the <tt>.csv</tt> file extension
71 # (Rails example: <tt><your-rails-app>/test/fixtures/web_sites.csv</tt>).
73 # The format of this type of fixture file is much more compact than the others, but also a little harder to read by us
74 # humans. The first line of the CSV file is a comma-separated list of field names. The rest of the file is then comprised
75 # of the actual data (1 per line). Here's an example:
78 # 1, Ruby On Rails, http://www.rubyonrails.org
79 # 2, Google, http://www.google.com
81 # Should you have a piece of data with a comma character in it, you can place double quotes around that value. If you
82 # need to use a double quote character, you must escape it with another double quote.
84 # Another unique attribute of the CSV fixture is that it has *no* fixture name like the other two formats. Instead, the
85 # fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
86 # number to the end. In our example, the 1st fixture would be called "web_site_1" and the 2nd one would be called
89 # Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
90 # have existing data somewhere already.
92 # = Single-file fixtures
94 # This type of fixture was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
95 # Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
96 # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
97 # put your files in <tt><your-rails-app>/test/fixtures/<your-model-name>/</tt> --
98 # like <tt><your-rails-app>/test/fixtures/web_sites/</tt> for the WebSite model).
100 # Each text file placed in this directory represents a "record". Usually these types of fixtures are named without
101 # extensions, but if you are on a Windows machine, you might consider adding <tt>.txt</tt> as the extension. Here's what the
102 # above example might look like:
105 # web_sites/yahoo.txt
106 # web_sites/ruby-on-rails
108 # The file format of a standard fixture is simple. Each line is a property (or column in db speak) and has the syntax
109 # of "name => value". Here's an example of the ruby-on-rails fixture above:
112 # name => Ruby on Rails
113 # url => http://www.rubyonrails.org
117 # Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
118 # fixtures, but first let's take a look at a sample unit test:
122 # class WebSiteTest < ActiveSupport::TestCase
123 # def test_web_site_count
124 # assert_equal 2, WebSite.count
128 # As it stands, unless we pre-load the web_site table in our database with two records, this test will fail. Here's the
129 # easiest way to add fixtures to the database:
132 # class WebSiteTest < ActiveSupport::TestCase
133 # fixtures :web_sites # add more by separating the symbols with commas
136 # By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here though), we trigger
137 # the testing environment to automatically load the appropriate fixtures into the database before each test.
138 # To ensure consistent data, the environment deletes the fixtures before running the load.
140 # In addition to being available in the database, the fixtures are also loaded into a hash stored in an instance variable
141 # of the test case. It is named after the symbol... so, in our example, there would be a hash available called
142 # <tt>@web_sites</tt>. This is where the "fixture name" comes into play.
144 # On top of that, each record is automatically "found" (using <tt>Model.find(id)</tt>) and placed in the instance variable of its name.
145 # So for the YAML fixtures, we'd get <tt>@rubyonrails</tt> and <tt>@google</tt>, which could be interrogated using regular Active Record semantics:
147 # # test if the object created from the fixture data has the same attributes as the data itself
149 # assert_equal @web_sites["rubyonrails"]["name"], @rubyonrails.name
152 # As seen above, the data hash created from the YAML fixtures would have <tt>@web_sites["rubyonrails"]["url"]</tt> return
153 # "http://www.rubyonrails.org" and <tt>@web_sites["google"]["name"]</tt> would return "Google". The same fixtures, but loaded
154 # from a CSV fixture file, would be accessible via <tt>@web_sites["web_site_1"]["name"] == "Ruby on Rails"</tt> and have the individual
155 # fixtures available as instance variables <tt>@web_site_1</tt> and <tt>@web_site_2</tt>.
157 # If you do not wish to use instantiated fixtures (usually for performance reasons) there are two options.
159 # - to completely disable instantiated fixtures:
160 # self.use_instantiated_fixtures = false
162 # - to keep the fixture instance (@web_sites) available, but do not automatically 'find' each instance:
163 # self.use_instantiated_fixtures = :no_instances
165 # Even if auto-instantiated fixtures are disabled, you can still access them
166 # by name via special dynamic methods. Each method has the same name as the
167 # model, and accepts the name of the fixture to instantiate:
169 # fixtures :web_sites
172 # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
175 # = Dynamic fixtures with ERb
177 # Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
178 # mix ERb in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like:
180 # <% for i in 1..1000 %>
186 # This will create 1000 very simple YAML fixtures.
188 # Using ERb, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
189 # This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable
190 # sample data. If you feel that you need to inject dynamic values, then perhaps you should reexamine whether your application
191 # is properly testable. Hence, dynamic values in fixtures are to be considered a code smell.
193 # = Transactional fixtures
195 # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
196 # They can also turn off auto-instantiation of fixture data since the feature is costly and often unused.
198 # class FooTest < ActiveSupport::TestCase
199 # self.use_transactional_fixtures = true
200 # self.use_instantiated_fixtures = false
205 # assert !Foo.find(:all).empty?
207 # assert Foo.find(:all).empty?
210 # def test_godzilla_aftermath
211 # assert !Foo.find(:all).empty?
215 # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
216 # then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes.
218 # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
219 # access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
221 # When *not* to use transactional fixtures:
222 # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
223 # particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
224 # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
225 # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
226 # Use InnoDB, MaxDB, or NDB instead.
228 # = Advanced YAML Fixtures
230 # YAML fixtures that don't specify an ID get some extra features:
232 # * Stable, autogenerated ID's
233 # * Label references for associations (belongs_to, has_one, has_many)
234 # * HABTM associations as inline lists
235 # * Autofilled timestamp columns
236 # * Fixture label interpolation
237 # * Support for YAML defaults
239 # == Stable, autogenerated ID's
241 # Here, have a monkey fixture:
245 # name: George the Monkey
249 # name: Reginald the Pirate
251 # Each of these fixtures has two unique identifiers: one for the database
252 # and one for the humans. Why don't we generate the primary key instead?
253 # Hashing each fixture's label yields a consistent ID:
255 # george: # generated id: 503576764
256 # name: George the Monkey
258 # reginald: # generated id: 324201669
259 # name: Reginald the Pirate
261 # Active Record looks at the fixture's model class, discovers the correct
262 # primary key, and generates it right before inserting the fixture
265 # The generated ID for a given label is constant, so we can discover
266 # any fixture's ID without loading anything, as long as we know the label.
268 # == Label references for associations (belongs_to, has_one, has_many)
270 # Specifying foreign keys in fixtures can be very fragile, not to
271 # mention difficult to read. Since Active Record can figure out the ID of
272 # any fixture from its label, you can specify FK's by label instead of ID.
276 # Let's break out some more monkeys and pirates.
282 # name: Reginald the Pirate
289 # name: George the Monkey
292 # Add a few more monkeys and pirates and break this into multiple files,
293 # and it gets pretty hard to keep track of what's going on. Let's
294 # use labels instead of ID's:
299 # name: Reginald the Pirate
305 # name: George the Monkey
308 # Pow! All is made clear. Active Record reflects on the fixture's model class,
309 # finds all the +belongs_to+ associations, and allows you to specify
310 # a target *label* for the *association* (monkey: george) rather than
311 # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
313 # ==== Polymorphic belongs_to
315 # Supporting polymorphic relationships is a little bit more complicated, since
316 # Active Record needs to know what type your association is pointing at. Something
317 # like this should look familiar:
321 # belongs_to :eater, :polymorphic => true
331 # Can we do better? You bet!
334 # eater: george (Monkey)
336 # Just provide the polymorphic target type and Active Record will take care of the rest.
338 # === has_and_belongs_to_many
340 # Time to give our monkey some fruit.
346 # name: George the Monkey
363 # ### in fruits_monkeys.yml
377 # Let's make the HABTM fixture go away.
382 # name: George the Monkey
384 # fruits: apple, orange, grape
397 # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
398 # on George's fixture, but we could've just as easily specified a list
399 # of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
400 # the fixture's model class and discovers the +has_and_belongs_to_many+
403 # == Autofilled timestamp columns
405 # If your table/model specifies any of Active Record's
406 # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
407 # they will automatically be set to <tt>Time.now</tt>.
409 # If you've set specific values, they'll be left alone.
411 # == Fixture label interpolation
413 # The label of the current fixture is always available as a column value:
416 # name: Geeksomnia's Account
419 # Also, sometimes (like when porting older join table fixtures) you'll need
420 # to be able to get ahold of the identifier for a given label. ERB
424 # monkey_id: <%= Fixtures.identify(:reginald) %>
425 # pirate_id: <%= Fixtures.identify(:george) %>
427 # == Support for YAML defaults
429 # You probably already know how to use YAML to set and reuse defaults in
430 # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
432 # DEFAULTS: &DEFAULTS
433 # created_on: <%= 3.weeks.ago.to_s(:db) %>
443 # Any fixture labeled "DEFAULTS" is safely ignored.
445 class Fixtures
< (RUBY_VERSION < '1.9' ? YAML
::Omap : Hash
)
446 DEFAULT_FILTER_RE
= /\.ya?ml$/
448 @
@all_cached_fixtures = {}
450 def self.reset_cache(connection
= nil)
451 connection
||= ActiveRecord
::Base.connection
452 @
@all_cached_fixtures[connection
.object_id
] = {}
455 def self.cache_for_connection(connection
)
456 @
@all_cached_fixtures[connection
.object_id
] ||= {}
457 @
@all_cached_fixtures[connection
.object_id
]
460 def self.fixture_is_cached
?(connection
, table_name
)
461 cache_for_connection(connection
)[table_name
]
464 def self.cached_fixtures(connection
, keys_to_fetch
= nil)
466 fixtures
= cache_for_connection(connection
).values_at(*keys_to_fetch
)
468 fixtures
= cache_for_connection(connection
).values
470 fixtures
.size
> 1 ? fixtures
: fixtures
.first
473 def self.cache_fixtures(connection
, fixtures_map
)
474 cache_for_connection(connection
).update(fixtures_map
)
477 def self.instantiate_fixtures(object
, table_name
, fixtures
, load_instances
= true)
478 object
.instance_variable_set
"@#{table_name.to_s.gsub('.','_')}", fixtures
480 ActiveRecord
::Base.silence
do
481 fixtures
.each
do |name
, fixture
|
483 object
.instance_variable_set
"@#{name}", fixture
.find
484 rescue FixtureClassNotFound
492 def self.instantiate_all_loaded_fixtures(object
, load_instances
= true)
493 all_loaded_fixtures
.each
do |table_name
, fixtures
|
494 Fixtures
.instantiate_fixtures(object
, table_name
, fixtures
, load_instances
)
498 cattr_accessor
:all_loaded_fixtures
499 self.all_loaded_fixtures
= {}
501 def self.create_fixtures(fixtures_directory
, table_names
, class_names
= {})
502 table_names
= [table_names
].flatten
.map
{ |n
| n
.to_s
}
503 connection
= block_given
? ? yield : ActiveRecord
::Base.connection
505 table_names_to_fetch
= table_names
.reject
{ |table_name
| fixture_is_cached
?(connection
, table_name
) }
507 unless table_names_to_fetch
.empty
?
508 ActiveRecord
::Base.silence
do
509 connection
.disable_referential_integrity
do
512 fixtures
= table_names_to_fetch
.map
do |table_name
|
513 fixtures_map
[table_name
] = Fixtures
.new(connection
, File
.split(table_name
.to_s
).last
, class_names
[table_name
.to_sym
], File
.join(fixtures_directory
, table_name
.to_s
))
516 all_loaded_fixtures
.update(fixtures_map
)
518 connection
.transaction(connection
.open_transactions
.zero
?) do
519 fixtures
.reverse
.each
{ |fixture
| fixture
.delete_existing_fixtures
}
520 fixtures
.each
{ |fixture
| fixture
.insert_fixtures
}
522 # Cap primary key sequences to max(pk).
523 if connection
.respond_to
?(:reset_pk_sequence!)
524 table_names
.each
do |table_name
|
525 connection
.reset_pk_sequence
!(table_name
)
530 cache_fixtures(connection
, fixtures_map
)
534 cached_fixtures(connection
, table_names
)
537 # Returns a consistent identifier for +label+. This will always
538 # be a positive integer, and will always be the same for a given
539 # label, assuming the same OS, platform, and version of Ruby.
540 def self.identify(label
)
544 attr_reader
:table_name, :name
546 def initialize(connection
, table_name
, class_name
, fixture_path
, file_filter
= DEFAULT_FILTER_RE
)
547 @connection, @table_name, @fixture_path, @file_filter = connection
, table_name
, fixture_path
, file_filter
548 @name = table_name
# preserve fixture base name
549 @class_name = class_name
||
550 (ActiveRecord
::Base.pluralize_table_names
? @table_name.singularize
.camelize
: @table_name.camelize
)
551 @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
552 @table_name = class_name
.table_name
if class_name
.respond_to
?(:table_name)
553 @connection = class_name
.connection
if class_name
.respond_to
?(:connection)
557 def delete_existing_fixtures
558 @connection.delete
"DELETE FROM #{@connection.quote_table_name(table_name)}", 'Fixture Delete'
562 now
= ActiveRecord
::Base.default_timezone
== :utc ? Time
.now
.utc
: Time
.now
565 # allow a standard key to be used for doing defaults in YAML
569 delete(assoc('DEFAULTS'))
572 # track any join tables we need to insert later
573 habtm_fixtures
= Hash
.new
do |h
, habtm
|
574 h
[habtm
] = HabtmFixtures
.new(@connection, habtm
.options
[:join_table], nil, nil)
577 each
do |label
, fixture
|
578 row
= fixture
.to_hash
580 if model_class
&& model_class
< ActiveRecord
::Base
581 # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
582 if model_class
.record_timestamps
583 timestamp_column_names
.each
do |name
|
584 row
[name
] = now
unless row
.key
?(name
)
588 # interpolate the fixture label
589 row
.each
do |key
, value
|
590 row
[key
] = label
if value
== "$LABEL"
593 # generate a primary key if necessary
594 if has_primary_key_column
? && !row
.include?(primary_key_name
)
595 row
[primary_key_name
] = Fixtures
.identify(label
)
598 # If STI is used, find the correct subclass for association reflection
600 if row
.include?(inheritance_column_name
)
601 row
[inheritance_column_name
].constantize
rescue model_class
606 reflection_class
.reflect_on_all_associations
.each
do |association
|
607 case association
.macro
609 # Do not replace association name with association foreign key if they are named the same
610 fk_name
= (association
.options
[:foreign_key] || "#{association.name}_id").to_s
612 if association
.name
.to_s
!= fk_name
&& value
= row
.delete(association
.name
.to_s
)
613 if association
.options
[:polymorphic]
614 if value
.sub
!(/\s*\(([^\)]*)\)\s*$/, "")
616 target_type_name
= (association
.options
[:foreign_type] || "#{association.name}_type").to_s
618 # support polymorphic belongs_to as "label (Type)"
619 row
[target_type_name
] = target_type
623 row
[fk_name
] = Fixtures
.identify(value
)
625 when :has_and_belongs_to_many
626 if (targets
= row
.delete(association
.name
.to_s
))
627 targets
= targets
.is_a
?(Array
) ? targets
: targets
.split(/\s*,\s*/)
628 join_fixtures
= habtm_fixtures
[association
]
630 targets
.each
do |target
|
631 join_fixtures
["#{label}_#{target}"] = Fixture
.new(
632 { association
.primary_key_name
=> row
[primary_key_name
],
633 association
.association_foreign_key
=> Fixtures
.identify(target
) }, nil)
640 @connection.insert_fixture(fixture
, @table_name)
643 # insert any HABTM join tables we discovered
644 habtm_fixtures
.values
.each
do |fixture
|
645 fixture
.delete_existing_fixtures
646 fixture
.insert_fixtures
651 class HabtmFixtures
< ::Fixtures #:nodoc:
652 def read_fixture_files
; end
656 unless defined?(@model_class)
658 if @class_name.nil? || @class_name.is_a
?(Class
)
661 @class_name.constantize
rescue nil
669 @primary_key_name ||= model_class
&& model_class
.primary_key
672 def has_primary_key_column
?
673 @has_primary_key_column ||= model_class
&& primary_key_name
&&
674 model_class
.columns
.find
{ |c
| c
.name
== primary_key_name
}
677 def timestamp_column_names
678 @timestamp_column_names ||= %w(created_at created_on updated_at updated_on
).select
do |name
|
679 column_names
.include?(name
)
683 def inheritance_column_name
684 @inheritance_column_name ||= model_class
&& model_class
.inheritance_column
688 @column_names ||= @connection.columns(@table_name).collect(&:name)
691 def read_fixture_files
692 if File
.file
?(yaml_file_path
)
693 read_yaml_fixture_files
694 elsif File
.file
?(csv_file_path
)
695 read_csv_fixture_files
699 def read_yaml_fixture_files
701 Dir
["#{@fixture_path}/**/*.yml"].select
{ |f
| test(?f
, f
) }.each
do |subfixture_path
|
702 yaml_string
<< IO
.read(subfixture_path
)
704 yaml_string
<< IO
.read(yaml_file_path
)
706 if yaml
= parse_yaml_string(yaml_string
)
707 # If the file is an ordered map, extract its children.
709 if yaml
.respond_to
?(:type_id) && yaml
.respond_to
?(:value)
715 yaml_value
.each
do |fixture
|
716 raise Fixture
::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture
.respond_to
?(:each)
717 fixture
.each
do |name
, data|
719 raise Fixture
::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
722 self[name
] = Fixture
.new(data, model_class
)
728 def read_csv_fixture_files
729 reader
= CSV
.parse(erb_render(IO
.read(csv_file_path
)))
730 header
= reader
.shift
734 row
.each_with_index
{ |cell
, j
| data[header
[j
].to_s
.strip
] = cell
.to_s
.strip
}
735 self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture
.new(data, model_class
)
740 "#{@fixture_path}.yml"
744 @fixture_path + ".csv"
747 def yaml_fixtures_key(path
)
748 File
.basename(@fixture_path).split(".").first
751 def parse_yaml_string(fixture_content
)
752 YAML
::load(erb_render(fixture_content
))
754 raise Fixture
::FormatError, "a YAML error occurred parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}"
757 def erb_render(fixture_content
)
758 ERB
.new(fixture_content
).result
762 class Fixture
#:nodoc:
765 class FixtureError
< StandardError
#:nodoc:
768 class FormatError
< FixtureError
#:nodoc:
771 attr_reader
:model_class
773 def initialize(fixture
, model_class
)
775 @model_class = model_class
.is_a
?(Class
) ? model_class
: model_class
.constantize
rescue nil
779 @model_class.name
if @model_class
783 @fixture.each
{ |item
| yield item
}
795 columns
= @fixture.keys
.collect
{ |column_name
| ActiveRecord
::Base.connection
.quote_column_name(column_name
) }
800 list
= @fixture.inject([]) do |fixtures
, (key
, value
)|
801 col
= model_class
.columns_hash
[key
] if model_class
.respond_to
?(:ancestors) && model_class
.ancestors
.include?(ActiveRecord
::Base)
802 fixtures
<< ActiveRecord
::Base.connection
.quote(value
, col
).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
809 model_class
.find(self[model_class
.primary_key
])
811 raise FixtureClassNotFound
, "No class attached to find."
818 class TestCase
#:nodoc:
819 setup
:setup_fixtures
820 teardown
:teardown_fixtures
822 superclass_delegating_accessor
:fixture_path
823 superclass_delegating_accessor
:fixture_table_names
824 superclass_delegating_accessor
:fixture_class_names
825 superclass_delegating_accessor
:use_transactional_fixtures
826 superclass_delegating_accessor
:use_instantiated_fixtures # true, false, or :no_instances
827 superclass_delegating_accessor
:pre_loaded_fixtures
829 self.fixture_table_names
= []
830 self.use_transactional_fixtures
= false
831 self.use_instantiated_fixtures
= true
832 self.pre_loaded_fixtures
= false
834 @
@already_loaded_fixtures = {}
835 self.fixture_class_names
= {}
838 def set_fixture_class(class_names
= {})
839 self.fixture_class_names
= self.fixture_class_names
.merge(class_names
)
842 def fixtures(*table_names
)
843 if table_names
.first
== :all
844 table_names
= Dir
["#{fixture_path}/*.yml"] + Dir
["#{fixture_path}/*.csv"]
845 table_names
.map
! { |f
| File
.basename(f
).split('.')[0..-2].join('.') }
847 table_names
= table_names
.flatten
.map
{ |n
| n
.to_s
}
850 self.fixture_table_names
|= table_names
851 require_fixture_classes(table_names
)
852 setup_fixture_accessors(table_names
)
855 def try_to_load_dependency(file_name
)
856 require_dependency file_name
857 rescue LoadError
=> e
858 # Let's hope the developer has included it himself
860 # Let's warn in case this is a subdependency, otherwise
861 # subdependency error messages are totally cryptic
862 if ActiveRecord
::Base.logger
863 ActiveRecord
::Base.logger
.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
867 def require_fixture_classes(table_names
= nil)
868 (table_names
|| fixture_table_names
).each
do |table_name
|
869 file_name
= table_name
.to_s
870 file_name
= file_name
.singularize
if ActiveRecord
::Base.pluralize_table_names
871 try_to_load_dependency(file_name
)
875 def setup_fixture_accessors(table_names
= nil)
876 table_names
= [table_names
] if table_names
&& !table_names
.respond_to
?(:each)
877 (table_names
|| fixture_table_names
).each
do |table_name
|
878 table_name
= table_name
.to_s
.tr('.', '_')
880 define_method(table_name
) do |*fixtures
|
881 force_reload
= fixtures
.pop
if fixtures
.last
== true || fixtures
.last
== :reload
883 @fixture_cache[table_name
] ||= {}
885 instances
= fixtures
.map
do |fixture
|
886 @fixture_cache[table_name
].delete(fixture
) if force_reload
888 if @loaded_fixtures[table_name
][fixture
.to_s
]
889 @fixture_cache[table_name
][fixture
] ||= @loaded_fixtures[table_name
][fixture
.to_s
].find
891 raise StandardError
, "No fixture with name '#{fixture}' found for table '#{table_name}'"
895 instances
.size
== 1 ? instances
.first
: instances
900 def uses_transaction(*methods
)
901 @uses_transaction = [] unless defined?(@uses_transaction)
902 @uses_transaction.concat methods
.map(&:to_s)
905 def uses_transaction
?(method
)
906 @uses_transaction = [] unless defined?(@uses_transaction)
907 @uses_transaction.include?(method
.to_s
)
911 def use_transactional_fixtures
?
912 use_transactional_fixtures
&&
913 !self.class.uses_transaction
?(method_name
)
917 return unless defined?(ActiveRecord
) && !ActiveRecord
::Base.configurations
.blank
?
919 if pre_loaded_fixtures
&& !use_transactional_fixtures
920 raise RuntimeError
, 'pre_loaded_fixtures requires use_transactional_fixtures'
925 # Load fixtures once and begin transaction.
926 if use_transactional_fixtures
?
927 if @
@already_loaded_fixtures[self.class]
928 @loaded_fixtures = @
@already_loaded_fixtures[self.class]
931 @
@already_loaded_fixtures[self.class] = @loaded_fixtures
933 ActiveRecord
::Base.connection
.increment_open_transactions
934 ActiveRecord
::Base.connection
.begin_db_transaction
935 # Load fixtures for every test.
938 @
@already_loaded_fixtures[self.class] = nil
942 # Instantiate fixtures for every test if requested.
943 instantiate_fixtures
if use_instantiated_fixtures
946 def teardown_fixtures
947 return unless defined?(ActiveRecord
) && !ActiveRecord
::Base.configurations
.blank
?
949 unless use_transactional_fixtures
?
953 # Rollback changes if a transaction is active.
954 if use_transactional_fixtures
? && ActiveRecord
::Base.connection
.open_transactions
!= 0
955 ActiveRecord
::Base.connection
.rollback_db_transaction
956 ActiveRecord
::Base.connection
.decrement_open_transactions
958 ActiveRecord
::Base.clear_active_connections
!
963 @loaded_fixtures = {}
964 fixtures
= Fixtures
.create_fixtures(fixture_path
, fixture_table_names
, fixture_class_names
)
966 if fixtures
.instance_of
?(Fixtures
)
967 @loaded_fixtures[fixtures
.name
] = fixtures
969 fixtures
.each
{ |f
| @loaded_fixtures[f
.name
] = f
}
974 # for pre_loaded_fixtures, only require the classes once. huge speed improvement
975 @
@required_fixture_classes = false
977 def instantiate_fixtures
978 if pre_loaded_fixtures
979 raise RuntimeError
, 'Load fixtures before instantiating them.' if Fixtures
.all_loaded_fixtures
.empty
?
980 unless @
@required_fixture_classes
981 self.class.require_fixture_classes Fixtures
.all_loaded_fixtures
.keys
982 @
@required_fixture_classes = true
984 Fixtures
.instantiate_all_loaded_fixtures(self, load_instances
?)
986 raise RuntimeError
, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
987 @loaded_fixtures.each
do |table_name
, fixtures
|
988 Fixtures
.instantiate_fixtures(self, table_name
, fixtures
, load_instances
?)
994 use_instantiated_fixtures
!= :no_instances