forked from jeremyevans/fixture_dependencies
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME
242 lines (174 loc) · 9.33 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
= fixture_dependencies
fixture_dependencies is an advanced fixture loader, allowing the loading of
models from YAML fixtures, along with their entire dependency graph. It has
the following features:
- Fixtures specify association names instead of foreign keys
- Support both Sequel and ActiveRecord
- Supports many_to_one/belongs_to, one_to_many/has_many,
many_to_many/has_and_belongs_to_many, and has_one associations
- Loads a fixture's dependency graph in such a manner that foreign key
constraints aren't violated
- Has a very simple API (FixtureDependencies.load(:model__fixture))
- Handles almost all cyclic dependencies
- Includes Rails and Sequel test helpers for Test::Unit that load fixtures for
every test inside a transaction, so fixture data is never left in your
database
== Installation
sudo gem install jeremyevans-fixture_dependencies \
--source http://gems.github.com
== Source
Source is available via github:
http://github.com/jeremyevans/fixture_dependencies
You can check it out with git:
git clone git://github.com/jeremyevans/fixture_dependencies.git
== Usage
=== With Rails/ActiveRecord/Test::Unit:
Add the following to test/test_helper.rb after "require 'test_help'":
require 'fixture_dependencies/test_unit/rails'
This overrides the default test helper to load the fixtures inside transactions
and to use FixtureDependencies to load the fixtures.
=== With Sequel/Test::Unit:
Somewhere before the test code is loaded:
require 'fixture_dependencies/test_unit/sequel'
Make sure the test case classes use FixtureDependencies::SequelTestCase:
class ModelTest < FixtureDependencies::SequelTestCase
This runs the test cases inside a Sequel transaction.
=== With other testing libraries:
You can just use FixtureDependencies.load to handle the loading of fixtures.
The use of transactions is up to you. It's fairly easy to add support for
running RSpec examples inside transactions. One thing you must do if you are
not using the rails test helper is to set the fixture path for
FixtureDependencies:
FixtureDependencies.fixture_path = '/path/to/fixtures'
== Changes to Rails default fixtures:
fixture_dependencies is designed to require the least possible changes to
the default YAML fixtures used by Rails (well, at least Rails 1.2 and earlier).
For example, see the following changes:
OLD NEW
asset1: asset1:
id: 1 id: 1
employee_id: 2 employee: jeremy
product_id: 3 product: nx7010
vendor_id: 2 vendor: lxg_computers
note: in working order note: in working order
As you can see, you just replace the foreign key attribute and value with the
name of the association and the associations name. This assumes you have an
employee fixture with a name of jeremy, and products fixture with the name of
nx7010, and a vendors fixture with the name lxg_computers.
Fixture files still use the table_name of the model.
== Changes to the fixtures Class Method:
fixture_dependencies can still use the fixtures class method in your test:
class EmployeeTest < Test::Unit::TestCase
fixtures :assets
end
In Rails default testing practices, the arguments to fixtures are table names.
fixture_dependencies changes this to underscored model names. If you are using
Rails' recommended table practices, this shouldn't make a difference.
It is recommended that you do not use the fixtures method, and instead load
individual fixtures as needed (see below). This makes your tests much more
robust, in case you want to add or remove individual fixtures at a later date.
== Loading individual fixtures with fixtures class method
There is support for loading individual fixtures (and just their dependencies),
using the following syntax:
class EmployeeTest < Test::Unit::TestCase
fixtures :employee__jeremy # Note the double underscore
end
This would load just the jeremy fixture and its dependencies. I find this is
much better than loading all fixtures in most of my test suites. Even better
is loading just the fixtures you want instead every test method (see below).
This leads to the most robust testing.
== Loading fixtures inside test methods
I find that it is often better to skip the use of the fixtures method entirely,
and load the fixtures I want manually in each test method. This provides for
the loosest coupling possible. Here's an example:
class EmployeeTest < Test::Unit::TestCase
def test_employee_name
# Load the fixture and return the Employee object
employee = load(:employee__jeremy)
# Test the employee
end
def test_award_statistics
# Load all fixtures in both tables
load(:employee_award__jeremy_first, :award__first)
# Test the award_statistics method
# (which pulls data from the tables loaded above)
end
end
Don't worry about loading the same fixture twice, if a fixture is already
loaded, it won't attempt to load it again.
== one_to_many/many_to_many/has_many/has_and_belongs_to_many assocations
Here's an example of using has_one (logon_information), has_many (assets), and
has_and_belongs_to_many (groups) associations.
jeremy:
id: 2
name: Jeremy Evans
logon_information: jeremy
assets: [asset1, asset2, asset3]
groups: [group1]
logon_information is a has_one association to another table which was split
from the employees table due to database security requirements. Assets is a
has_many association, where one employee is responsible for the asset.
Employees can be a member of multiple groups, and each group can have multiple
employees.
For has_* associations, after fixture_dependencies saves jeremy, it will load
and save logon_information (and its dependencies...), it will load each asset
in the order specified (and their dependencies...), and it will load all of the
groups in the order specified (and their dependencies...). Note that there
is only a load order inside a specific association, associations are stored
in the same hash as attributes and are loaded in an arbitrary order.
== many_to_many/has_and_belongs_to_many join table fixtures
Another change is that Rails defaults allow you to specify habtm join tables in
fixtures. That doesn't work with fixture dependencies, as there is no
associated model. Instead, you use a has_and_belongs_to_many association name
in the the appropriate model fixtures (see above).
== Cyclic dependencies
fixture_dependencies handles almost all cyclic dependencies. It handles all
has_many, has_one, and habtm cyclic dependencies. It handles all
self-referential cyclic dependencies. It handles all belongs_to cyclic
dependencies except the case where there is a NOT NULL or validates_presence of
constraint on the cyclic dependency's foreign key.
For example, a case that won't work is when employee belongs_to supervisor
(with a NOT NULL or validates_presence_of constraint on supervisor_id), and
john is karl's supervisor and karl is john's supervisor. Since you can't create
john without a valid supervisor_id, you need to create karl first, but you
can't create karl for the same reason (as john doesn't exist yet).
There isn't a generic way to handle the belongs_to cyclic dependency, as far as
I know. Deferring foreign key checks could work, but may not be enabled (and
one of the main reasons to use the plugin is that it doesn't require them).
For associations like the example above (employee's supervisor is also an
employee), setting the foreign_key to the primary key and then changing it
later is an option, but database checks may prevent it. For more complex
cyclic dependencies involving multiple model classes (employee belongs_to
division belongs_to head_of_division when the employee is a member of the
division and also the head of the division), even that approach is not
possible.
== Known issues
Currently, the plugin only supports YAML fixtures, but other types of fixtures
would be fairly easy to add (send me a patch if you add support for another
fixture type).
The plugin is significantly slower than the default testing method, because it
loads all fixtures inside of a transaction (one per test method), where Rails
defaults to loading the fixtures once per test suite (outside of a
transaction), and only deletes fixtures from a table when overwriting it with
new fixtures. Rails actually did something similar starting with r2714, but it
was rolled back in r2730 due to speed issues. See ticket #2404 on Rails' trac.
Instantiated fixtures are not available with this plugin. Instead, you should
use load(:model__fixture_name).
== Troubleshooting
If you run into problems with loading your fixtures, it can be difficult to see
where the problems are. To aid in debugging an error, add the following to
test/test_helper.rb:
FixtureDependencies.verbose = 3
This will give a verbose description of the loading and saving of fixtures for
every test, including the recursive loading of the dependency graph.
== Similar Ideas
Rails now supports something similar by default. Honestly, I'm not sure what
the differences are.
fixture_references is a similar plugin. It uses erb inside yaml, and uses the
foreign key numbers inside of the association names, which leads me to believe
it doesn't support has_* associations.
== License
fixture_dependencies is released under the MIT License. See the LICENSE file
for details.
== Author
Jeremy Evans <[email protected]>