jonysk-factory_girl 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 Joe Ferris and thoughtbot, inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,130 @@
1
+ h1. factory_girl
2
+
3
+ Written by "Joe Ferris":mailto:jferris@thoughtbot.com.
4
+
5
+ Thanks to Tammer Saleh, Dan Croak, and Jon Yurek of thoughtbot, inc.
6
+
7
+ Copyright 2008 Joe Ferris and thoughtbot, inc.
8
+
9
+ h2. Download
10
+
11
+ Github: "Page":http://github.com/thoughtbot/factory_girl/tree/master "Clone":git://github.com/thoughtbot/factory_girl.git
12
+
13
+ Gem: <pre>gem install thoughtbot-factory_girl --source http://gems.github.com</pre>
14
+
15
+ h2. Defining factories
16
+
17
+ <pre><code># This will guess the User class
18
+ Factory.define :user do |u|
19
+ u.first_name 'John'
20
+ u.last_name 'Doe'
21
+ u.admin false
22
+ end
23
+
24
+ # This will use the User class (Admin would have been guessed)
25
+ Factory.define :admin, :class => User do |u|
26
+ u.first_name 'Admin'
27
+ u.last_name 'User'
28
+ u.admin true
29
+ end</code></pre>
30
+
31
+
32
+ It is recommended that you create a test/factories.rb file and define your
33
+ factories there. This file can be included from test_helper or directly from
34
+ your test files. Don't forget:
35
+ <pre><code>require 'factory_girl'</code></pre>
36
+
37
+
38
+ h2. Lazy Attributes
39
+
40
+ Most attributes can be added using static values that are evaluated when the
41
+ factory is defined, but some attributes (such as associations and other
42
+ attributes that must be dynamically generated) will need values assigned each
43
+ time an instance is generated. These "lazy" attributes can be added by passing
44
+ a block instead of a parameter:
45
+
46
+ <pre><code>Factory.define :user do |u|
47
+ # ...
48
+ u.activation_code { User.generate_activation_code }
49
+ end</code></pre>
50
+
51
+
52
+ h2. Dependent Attributes
53
+
54
+ Some attributes may need to be generated based on the values of other
55
+ attributes. This can be done by calling the attribute name on
56
+ Factory::AttributeProxy, which is yielded to lazy attribute blocks:
57
+
58
+ <pre><code>Factory.define :user do |u|
59
+ u.first_name 'Joe'
60
+ u.last_name 'Blow'
61
+ u.email {|a| "#{a.first_name}.#{a.last_name}@example.com".downcase }
62
+ end
63
+
64
+ Factory(:user, :last_name => 'Doe').email
65
+ # => "joe.doe@example.com"</code></pre>
66
+
67
+
68
+ h2. Associations
69
+
70
+ Associated instances can be generated by using the association method when
71
+ defining a lazy attribute:
72
+
73
+ <pre><code>Factory.define :post do |p|
74
+ # ...
75
+ p.author {|author| author.association(:user, :last_name => 'Writely') }
76
+ end</code></pre>
77
+
78
+
79
+ When using the association method, the same build strategy (build, create, construct, or attributes_for) will be used for all generated instances:
80
+
81
+ <pre><code># Builds and saves a User and a Post
82
+ post = Factory(:post)
83
+ post.new_record? # => false
84
+ post.author.new_record # => false
85
+
86
+ # Builds and saves a User and a Post (does not raise error if the object wasn't saved)
87
+ post = Factory.construct(:post)
88
+ post.new_record? # => false
89
+ post.author.new_record # => false
90
+
91
+ # Builds but does not save a User and a Post
92
+ Factory.build(:post)
93
+ post.new_record? # => true
94
+ post.author.new_record # => true</code></pre>
95
+
96
+
97
+ h2. Sequences
98
+
99
+ Unique values in a specific format (for example, e-mail addresses) can be
100
+ generated using sequences. Sequences are defined by calling Factory.sequence,
101
+ and values in a sequence are generated by calling Factory.next:
102
+
103
+ <pre><code># Defines a new sequence
104
+ Factory.sequence :email do |n|
105
+ "person#{n}@example.com"
106
+ end
107
+
108
+ Factory.next :email
109
+ # => "person1@example.com"
110
+
111
+ Factory.next :email
112
+ # => "person2@example.com"</code></pre>
113
+
114
+
115
+ h2. Using factories
116
+
117
+ <pre><code># Build and save a User instance
118
+ Factory(:user)
119
+
120
+ # Build a User instance and override the first_name property
121
+ Factory.build(:user, :first_name => 'Joe')
122
+
123
+ # Return an attributes Hash that can be used to build a User instance
124
+ attrs = Factory.attributes_for(:user)</code></pre>
125
+
126
+ h2. More Information
127
+
128
+ "Our blog":http://giantrobots.thoughtbot.com
129
+
130
+ "factory_girl rdoc":http://dev.thoughtbot.com/factory_girl
@@ -0,0 +1,69 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/gempackagetask'
6
+ require 'date'
7
+
8
+ desc 'Default: run unit tests.'
9
+ task :default => :test
10
+
11
+ desc 'Test the factory_girl plugin.'
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'lib'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = true
16
+ end
17
+
18
+ desc 'Generate documentation for the factory_girl plugin.'
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'Factory Girl'
22
+ rdoc.options << '--line-numbers' << '--inline-source' << "--main" << "README.textile"
23
+ rdoc.rdoc_files.include('README.textile')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+
27
+ desc 'Update documentation on website'
28
+ task :sync_docs => 'rdoc' do
29
+ `rsync -ave ssh rdoc/ dev@dev.thoughtbot.com:/home/dev/www/dev.thoughtbot.com/factory_girl`
30
+ end
31
+
32
+ spec = Gem::Specification.new do |s|
33
+ s.name = %q{factory_girl}
34
+ s.version = "1.1.2"
35
+ s.summary = %q{factory_girl provides a framework and DSL for defining and
36
+ using model instance factories.}
37
+ s.description = %q{factory_girl provides a framework and DSL for defining and
38
+ using factories - less error-prone, more explicit, and
39
+ all-around easier to work with than fixtures.}
40
+
41
+ s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'test/**/*.rb']
42
+ s.require_path = 'lib'
43
+ s.test_files = Dir[*['test/**/*_test.rb']]
44
+
45
+ s.has_rdoc = true
46
+ s.extra_rdoc_files = ["README.textile"]
47
+ s.rdoc_options = ['--line-numbers', '--inline-source', "--main", "README.textile"]
48
+
49
+ s.authors = ["Joe Ferris"]
50
+ s.email = %q{jferris@thoughtbot.com}
51
+
52
+ s.platform = Gem::Platform::RUBY
53
+ s.add_dependency(%q<activesupport>, [">= 1.0"])
54
+ end
55
+
56
+ Rake::GemPackageTask.new spec do |pkg|
57
+ pkg.need_tar = true
58
+ pkg.need_zip = true
59
+ end
60
+
61
+ desc "Clean files generated by rake tasks"
62
+ task :clobber => [:clobber_rdoc, :clobber_package]
63
+
64
+ desc "Generate a gemspec file"
65
+ task :gemspec do
66
+ File.open("#{spec.name}.gemspec", 'w') do |f|
67
+ f.write spec.to_ruby
68
+ end
69
+ end
@@ -0,0 +1,12 @@
1
+ require 'activesupport'
2
+ require 'factory_girl/factory'
3
+ require 'factory_girl/attribute_proxy'
4
+ require 'factory_girl/sequence'
5
+
6
+ # Shortcut for Factory.create.
7
+ #
8
+ # Example:
9
+ # Factory(:user, :name => 'Joe')
10
+ def Factory (name, attrs = {})
11
+ Factory.create(name, attrs)
12
+ end
@@ -0,0 +1,92 @@
1
+ class Factory
2
+
3
+ class AttributeProxy
4
+
5
+ attr_accessor :factory, :attribute_name, :strategy, :current_values #:nodoc:
6
+
7
+ def initialize (factory, attr, strategy, values) #:nodoc:
8
+ @factory = factory
9
+ @attribute_name = attr
10
+ @strategy = strategy
11
+ @current_values = values
12
+ end
13
+
14
+ # Generates an association using the current build strategy.
15
+ #
16
+ # Arguments:
17
+ # name: (Symbol)
18
+ # The name of the factory that should be used to generate this
19
+ # association.
20
+ # attributes: (Hash)
21
+ # A hash of attributes that should be overridden for this association.
22
+ #
23
+ # Returns:
24
+ # The generated association for the current build strategy. Note that
25
+ # assocaitions are not generated for the attributes_for strategy. Returns
26
+ # nil in this case.
27
+ #
28
+ # Example:
29
+ #
30
+ # Factory.define :user do |f|
31
+ # # ...
32
+ # end
33
+ #
34
+ # Factory.define :post do |f|
35
+ # # ...
36
+ # f.author {|a| a.association :user, :name => 'Joe' }
37
+ # end
38
+ #
39
+ # # Builds (but doesn't save) a Post and a User
40
+ # Factory.build(:post)
41
+ #
42
+ # # Builds and saves a User, builds a Post, assigns the User to the
43
+ # # author association, and saves the User.
44
+ # Factory.create(:post)
45
+ #
46
+ def association (name, attributes = {})
47
+ if strategy == :attributes_for
48
+ nil
49
+ else
50
+ Factory.send(strategy, name, attributes)
51
+ end
52
+ end
53
+
54
+ # Returns the value for specified attribute. A value will only be available
55
+ # if it was overridden when calling the factory, or if a value is added
56
+ # earlier in the definition of the factory.
57
+ #
58
+ # Arguments:
59
+ # attribute: (Symbol)
60
+ # The attribute whose value should be returned.
61
+ #
62
+ # Returns:
63
+ # The value of the requested attribute. (Object)
64
+ def value_for (attribute)
65
+ unless current_values.key?(attribute)
66
+ raise ArgumentError, "No such attribute: #{attribute.inspect}"
67
+ end
68
+ current_values[attribute]
69
+ end
70
+
71
+ # Undefined methods are delegated to value_for, which means that:
72
+ #
73
+ # Factory.define :user do |f|
74
+ # f.first_name 'Ben'
75
+ # f.last_name {|a| a.value_for(:first_name) }
76
+ # end
77
+ #
78
+ # and:
79
+ #
80
+ # Factory.define :user do |f|
81
+ # f.first_name 'Ben'
82
+ # f.last_name {|a| a.first_name }
83
+ # end
84
+ #
85
+ # are equivilent.
86
+ def method_missing (name, *args, &block)
87
+ current_values[name]
88
+ end
89
+
90
+ end
91
+
92
+ end
@@ -0,0 +1,239 @@
1
+ class Factory
2
+
3
+ cattr_accessor :factories, :sequences #:nodoc:
4
+ self.factories = {}
5
+ self.sequences = {}
6
+
7
+ attr_reader :factory_name
8
+
9
+ # Defines a new factory that can be used by the build strategies (create and
10
+ # build) to build new objects.
11
+ #
12
+ # Arguments:
13
+ # name: (Symbol)
14
+ # A unique name used to identify this factory.
15
+ # options: (Hash)
16
+ # class: the class that will be used when generating instances for this
17
+ # factory. If not specified, the class will be guessed from the
18
+ # factory name.
19
+ #
20
+ # Yields:
21
+ # The newly created factory (Factory)
22
+ def self.define (name, options = {})
23
+ instance = Factory.new(name, options)
24
+ yield(instance)
25
+ self.factories[name] = instance
26
+ end
27
+
28
+ # Defines a new sequence that can be used to generate unique values in a specific format.
29
+ #
30
+ # Arguments:
31
+ # name: (Symbol)
32
+ # A unique name for this sequence. This name will be referenced when
33
+ # calling next to generate new values from this sequence.
34
+ # block: (Proc)
35
+ # The code to generate each value in the sequence. This block will be
36
+ # called with a unique number each time a value in the sequence is to be
37
+ # generated. The block should return the generated value for the
38
+ # sequence.
39
+ #
40
+ # Example:
41
+ #
42
+ # Factory.sequence(:email) {|n| "somebody_#{n}@example.com" }
43
+ def self.sequence (name, &block)
44
+ self.sequences[name] = Sequence.new(&block)
45
+ end
46
+
47
+ # Generates and returns the next value in a sequence.
48
+ #
49
+ # Arguments:
50
+ # name: (Symbol)
51
+ # The name of the sequence that a value should be generated for.
52
+ #
53
+ # Returns:
54
+ # The next value in the sequence. (Object)
55
+ def self.next (sequence)
56
+ unless self.sequences.key?(sequence)
57
+ raise "No such sequence: #{sequence}"
58
+ end
59
+
60
+ self.sequences[sequence].next
61
+ end
62
+
63
+ def build_class #:nodoc:
64
+ @build_class ||= @options[:class] || factory_name.to_s.classify.constantize
65
+ end
66
+
67
+ def initialize (name, options = {}) #:nodoc:
68
+ options.assert_valid_keys(:class)
69
+ @factory_name = name
70
+ @options = options
71
+
72
+ @static_attributes = {}
73
+ @lazy_attribute_blocks = {}
74
+ @lazy_attribute_names = []
75
+ end
76
+
77
+ # Adds an attribute that should be assigned on generated instances for this
78
+ # factory.
79
+ #
80
+ # This method should be called with either a value or block, but not both. If
81
+ # called with a block, the attribute will be generated "lazily," whenever an
82
+ # instance is generated. Lazy attribute blocks will not be called if that
83
+ # attribute is overriden for a specific instance.
84
+ #
85
+ # When defining lazy attributes, an instance of Factory::AttributeProxy will
86
+ # be yielded, allowing associations to be built using the correct build
87
+ # strategy.
88
+ #
89
+ # Arguments:
90
+ # name: (Symbol)
91
+ # The name of this attribute. This will be assigned using :"#{name}=" for
92
+ # generated instances.
93
+ # value: (Object)
94
+ # If no block is given, this value will be used for this attribute.
95
+ def add_attribute (name, value = nil, &block)
96
+ if block_given?
97
+ unless value.nil?
98
+ raise ArgumentError, "Both value and block given"
99
+ end
100
+ @lazy_attribute_blocks[name] = block
101
+ @lazy_attribute_names << name
102
+ else
103
+ @static_attributes[name] = value
104
+ end
105
+ end
106
+
107
+ # Calls add_attribute using the missing method name as the name of the
108
+ # attribute, so that:
109
+ #
110
+ # Factory.define :user do |f|
111
+ # f.name 'Billy Idol'
112
+ # end
113
+ #
114
+ # and:
115
+ #
116
+ # Factory.define :user do |f|
117
+ # f.add_attribute :name, 'Billy Idol'
118
+ # end
119
+ #
120
+ # are equivilent.
121
+ def method_missing (name, *args, &block)
122
+ add_attribute(name, *args, &block)
123
+ end
124
+
125
+ def attributes_for (attrs = {}) #:nodoc:
126
+ build_attributes_hash(attrs, :attributes_for)
127
+ end
128
+
129
+ def build (attrs = {}) #:nodoc:
130
+ build_instance(attrs, :build)
131
+ end
132
+
133
+ def create (attrs = {}) #:nodoc:
134
+ instance = build_instance(attrs, :create)
135
+ instance.save!
136
+ instance
137
+ end
138
+
139
+ def construct (attrs = {}) #:nodoc:
140
+ instance = build_instance(attrs, :create)
141
+ instance.save
142
+ instance
143
+ end
144
+
145
+ class << self
146
+
147
+ # Generates and returns a Hash of attributes from this factory. Attributes
148
+ # can be individually overridden by passing in a Hash of attribute => value
149
+ # pairs.
150
+ #
151
+ # Arguments:
152
+ # attrs: (Hash)
153
+ # Attributes to overwrite for this set.
154
+ #
155
+ # Returns:
156
+ # A set of attributes that can be used to build an instance of the class
157
+ # this factory generates. (Hash)
158
+ def attributes_for (name, attrs = {})
159
+ factory_by_name(name).attributes_for(attrs)
160
+ end
161
+
162
+ # Generates and returns an instance from this factory. Attributes can be
163
+ # individually overridden by passing in a Hash of attribute => value pairs.
164
+ #
165
+ # Arguments:
166
+ # attrs: (Hash)
167
+ # See attributes_for
168
+ #
169
+ # Returns:
170
+ # An instance of the class this factory generates, with generated
171
+ # attributes assigned.
172
+ def build (name, attrs = {})
173
+ factory_by_name(name).build(attrs)
174
+ end
175
+
176
+ # Generates, saves, and returns an instance from this factory. Attributes can
177
+ # be individually overridden by passing in a Hash of attribute => value
178
+ # pairs.
179
+ #
180
+ # If the instance is not valid, an ActiveRecord::Invalid exception will be
181
+ # raised.
182
+ #
183
+ # Arguments:
184
+ # attrs: (Hash)
185
+ # See attributes_for
186
+ #
187
+ # Returns:
188
+ # A saved instance of the class this factory generates, with generated
189
+ # attributes assigned.
190
+ def create (name, attrs = {})
191
+ factory_by_name(name).create(attrs)
192
+ end
193
+
194
+ # Generates, saves, and returns an instance from this factory. Attributes can
195
+ # be individually overridden by passing in a Hash of attribute => value
196
+ # pairs.
197
+ #
198
+ # If the instance is not valid, does not raise an ActiveRecord::Invalid exception (differently from create())
199
+ #
200
+ # Arguments:
201
+ # attrs: (Hash)
202
+ # See attributes_for
203
+ #
204
+ # Returns:
205
+ # A saved instance of the class this factory generates, with generated
206
+ # attributes assigned.
207
+ def construct (name, attrs = {})
208
+ factory_by_name(name).construct(attrs)
209
+ end
210
+
211
+ private
212
+
213
+ def factory_by_name (name)
214
+ factories[name] or raise ArgumentError.new("No such factory: #{name.inspect}")
215
+ end
216
+
217
+ end
218
+
219
+ private
220
+
221
+ def build_attributes_hash (override, strategy)
222
+ result = @static_attributes.merge(override)
223
+ @lazy_attribute_names.each do |name|
224
+ proxy = AttributeProxy.new(self, name, strategy, result)
225
+ result[name] = @lazy_attribute_blocks[name].call(proxy) unless override.key?(name)
226
+ end
227
+ result
228
+ end
229
+
230
+ def build_instance (override, strategy)
231
+ instance = build_class.new
232
+ attrs = build_attributes_hash(override, strategy)
233
+ attrs.each do |attr, value|
234
+ instance.send(:"#{attr}=", value)
235
+ end
236
+ instance
237
+ end
238
+
239
+ end
@@ -0,0 +1,17 @@
1
+ class Factory
2
+
3
+ class Sequence
4
+
5
+ def initialize (&proc)
6
+ @proc = proc
7
+ @value = 0
8
+ end
9
+
10
+ def next
11
+ @value += 1
12
+ @proc.call(@value)
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,101 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class AttributeProxyTest < Test::Unit::TestCase
4
+
5
+ context "an association proxy" do
6
+
7
+ setup do
8
+ @factory = mock('factory')
9
+ @attr = :user
10
+ @attrs = { :first_name => 'John' }
11
+ @strategy = :create
12
+ @proxy = Factory::AttributeProxy.new(@factory, @attr, @strategy, @attrs)
13
+ end
14
+
15
+ should "have a factory" do
16
+ assert_equal @factory, @proxy.factory
17
+ end
18
+
19
+ should "have an attribute name" do
20
+ assert_equal @attr, @proxy.attribute_name
21
+ end
22
+
23
+ should "have a build strategy" do
24
+ assert_equal @strategy, @proxy.strategy
25
+ end
26
+
27
+ should "have attributes" do
28
+ assert_equal @attrs, @proxy.current_values
29
+ end
30
+
31
+ context "building an association" do
32
+
33
+ setup do
34
+ @association = mock('built-user')
35
+ @name = :user
36
+ @attribs = { :first_name => 'Billy' }
37
+
38
+ Factory.stubs(@strategy).returns(@association)
39
+ end
40
+
41
+ should "delegate to the appropriate method on Factory" do
42
+ Factory.expects(@strategy).with(@name, @attribs).returns(@association)
43
+ @proxy.association(@name, @attribs)
44
+ end
45
+
46
+ should "return the built association" do
47
+ assert_equal @association, @proxy.association(@name)
48
+ end
49
+
50
+ end
51
+
52
+ context "building an association using the attributes_for strategy" do
53
+
54
+ setup do
55
+ @strategy = :attributes_for
56
+ @proxy = Factory::AttributeProxy.new(@factory, @attr, @strategy, @attrs)
57
+ end
58
+
59
+ should "not build the association" do
60
+ Factory.expects(@strategy).never
61
+ @proxy.association(:user)
62
+ end
63
+
64
+ should "return nil for the association" do
65
+ Factory.stubs(@strategy).returns(:user)
66
+ assert_nil @proxy.association(:user)
67
+ end
68
+
69
+ end
70
+
71
+ context "fetching the value of an attribute" do
72
+
73
+ setup do
74
+ @attr = :first_name
75
+ end
76
+
77
+ should "return the correct value" do
78
+ assert_equal @attrs[@attr], @proxy.value_for(@attr)
79
+ end
80
+
81
+ should "call value_for for undefined methods" do
82
+ assert_equal @attrs[@attr], @proxy.send(@attr)
83
+ end
84
+
85
+ end
86
+
87
+ context "fetching the value of an undefined attribute" do
88
+
89
+ setup do
90
+ @attr = :beachball
91
+ end
92
+
93
+ should "raise an ArgumentError" do
94
+ assert_raise(ArgumentError) { @proxy.value_for(@attr) }
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+
101
+ end
@@ -0,0 +1,353 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class FactoryTest < Test::Unit::TestCase
4
+
5
+ def self.should_instantiate_class
6
+
7
+ should "instantiate the build class" do
8
+ assert_kind_of @class, @instance
9
+ end
10
+
11
+ should "assign attributes on the instance" do
12
+ assert_equal @first_name, @instance.first_name
13
+ assert_equal @last_name, @instance.last_name
14
+ end
15
+
16
+ should "override attributes using the passed hash" do
17
+ @value = 'Davis'
18
+ @instance = @factory.build(:first_name => @value)
19
+ assert_equal @value, @instance.first_name
20
+ end
21
+
22
+ end
23
+
24
+ context "defining a factory" do
25
+
26
+ setup do
27
+ @name = :user
28
+ @factory = mock('factory')
29
+ @options = { :class => 'magic' }
30
+ Factory.stubs(:new).returns(@factory)
31
+ end
32
+
33
+ should "create a new factory using the specified name and options" do
34
+ Factory.expects(:new).with(@name, @options)
35
+ Factory.define(@name, @options) {|f| }
36
+ end
37
+
38
+ should "pass the factory do the block" do
39
+ yielded = nil
40
+ Factory.define(@name) do |y|
41
+ yielded = y
42
+ end
43
+ assert_equal @factory, yielded
44
+ end
45
+
46
+ should "add the factory to the list of factories" do
47
+ Factory.define(@name) {|f| }
48
+ assert_equal Factory.factories[@name],
49
+ @factory,
50
+ "Factories: #{Factory.factories.inspect}"
51
+ end
52
+
53
+ end
54
+
55
+ context "defining a sequence" do
56
+
57
+ setup do
58
+ @sequence = mock('sequence')
59
+ @name = :count
60
+ Factory::Sequence.stubs(:new).returns(@sequence)
61
+ end
62
+
63
+ should "create a new sequence" do
64
+ Factory::Sequence.expects(:new).with().returns(@sequence)
65
+ Factory.sequence(@name)
66
+ end
67
+
68
+ should "use the supplied block as the sequence generator" do
69
+ Factory::Sequence.stubs(:new).yields(1)
70
+ yielded = false
71
+ Factory.sequence(@name) {|n| yielded = true }
72
+ assert yielded
73
+ end
74
+
75
+ end
76
+
77
+ context "a factory" do
78
+
79
+ setup do
80
+ @name = :user
81
+ @class = User
82
+ @factory = Factory.new(@name)
83
+ end
84
+
85
+ should "have a factory name" do
86
+ assert_equal @name, @factory.factory_name
87
+ end
88
+
89
+ should "have a build class" do
90
+ assert_equal @class, @factory.build_class
91
+ end
92
+
93
+ context "when adding an attribute with a value parameter" do
94
+
95
+ setup do
96
+ @attr = :name
97
+ @value = 'Elvis lives!'
98
+ @factory.add_attribute(@attr, @value)
99
+ end
100
+
101
+ should "include that value in the generated attributes hash" do
102
+ assert_equal @value, @factory.attributes_for[@attr]
103
+ end
104
+
105
+ end
106
+
107
+ context "when adding an attribute with a block" do
108
+
109
+ setup do
110
+ @attr = :name
111
+ @attrs = {}
112
+ @proxy = mock('attr-proxy')
113
+ Factory::AttributeProxy.stubs(:new).returns(@proxy)
114
+ end
115
+
116
+ should "not evaluate the block when the attribute is added" do
117
+ @factory.add_attribute(@attr) { flunk }
118
+ end
119
+
120
+ should "evaluate the block when attributes are generated" do
121
+ called = false
122
+ @factory.add_attribute(@attr) do
123
+ called = true
124
+ end
125
+ @factory.attributes_for
126
+ assert called
127
+ end
128
+
129
+ should "use the result of the block as the value of the attribute" do
130
+ value = "Watch out for snakes!"
131
+ @factory.add_attribute(@attr) { value }
132
+ assert_equal value, @factory.attributes_for[@attr]
133
+ end
134
+
135
+ should "build an attribute proxy" do
136
+ Factory::AttributeProxy.expects(:new).with(@factory, @attr, :attributes_for, @attrs)
137
+ @factory.add_attribute(@attr) {}
138
+ @factory.attributes_for
139
+ end
140
+
141
+ should "yield an attribute proxy to the block" do
142
+ yielded = nil
143
+ @factory.add_attribute(@attr) {|y| yielded = y }
144
+ @factory.attributes_for
145
+ assert_equal @proxy, yielded
146
+ end
147
+
148
+ context "when other attributes have previously been defined" do
149
+
150
+ setup do
151
+ @attr = :unimportant
152
+ @attrs = {
153
+ :one => 'whatever',
154
+ :another => 'soup'
155
+ }
156
+ @factory.add_attribute(:one, 'whatever')
157
+ @factory.add_attribute(:another) { 'soup' }
158
+ @factory.add_attribute(@attr) {}
159
+ end
160
+
161
+ should "provide previously set attributes" do
162
+ Factory::AttributeProxy.expects(:new).with(@factory, @attr, :attributes_for, @attrs)
163
+ @factory.attributes_for
164
+ end
165
+
166
+ end
167
+
168
+ end
169
+
170
+ should "add an attribute using the method name when passed an undefined method" do
171
+ @attr = :first_name
172
+ @value = 'Sugar'
173
+ @factory.send(@attr, @value)
174
+ assert_equal @value, @factory.attributes_for[@attr]
175
+ end
176
+
177
+ should "not allow attributes to be added with both a value parameter and a block" do
178
+ assert_raise(ArgumentError) do
179
+ @factory.add_attribute(:name, 'value') {}
180
+ end
181
+ end
182
+
183
+ context "when overriding generated attributes with a hash" do
184
+
185
+ setup do
186
+ @attr = :name
187
+ @value = 'The price is right!'
188
+ @hash = { @attr => @value }
189
+ end
190
+
191
+ should "return the overridden value in the generated attributes" do
192
+ @factory.add_attribute(@attr, 'The price is wrong, Bob!')
193
+ assert_equal @value, @factory.attributes_for(@hash)[@attr]
194
+ end
195
+
196
+ should "not call a lazy attribute block for an overridden attribute" do
197
+ @factory.add_attribute(@attr) { flunk }
198
+ @factory.attributes_for(@hash)
199
+ end
200
+
201
+ end
202
+
203
+ should "guess the build class from the factory name" do
204
+ assert_equal User, @factory.build_class
205
+ end
206
+
207
+ context "when defined with a custom class" do
208
+
209
+ setup do
210
+ @class = User
211
+ @factory = Factory.new(:author, :class => @class)
212
+ end
213
+
214
+ should "use the specified class as the build class" do
215
+ assert_equal @class, @factory.build_class
216
+ end
217
+
218
+ end
219
+
220
+ context "with some attributes added" do
221
+
222
+ setup do
223
+ @first_name = 'Billy'
224
+ @last_name = 'Idol'
225
+ @email = 'test@something.com'
226
+
227
+ @factory.add_attribute(:first_name, @first_name)
228
+ @factory.add_attribute(:last_name, @last_name)
229
+ @factory.add_attribute(:email, @email)
230
+ end
231
+
232
+ context "when building an instance" do
233
+
234
+ setup do
235
+ @instance = @factory.build
236
+ end
237
+
238
+ should_instantiate_class
239
+
240
+ should "not save the instance" do
241
+ assert @instance.new_record?
242
+ end
243
+
244
+ end
245
+
246
+ context "when creating an instance" do
247
+
248
+ setup do
249
+ @instance = @factory.create
250
+ end
251
+
252
+ should_instantiate_class
253
+
254
+ should "save the instance" do
255
+ assert !@instance.new_record?
256
+ end
257
+
258
+ end
259
+
260
+ should "raise an ActiveRecord::RecordInvalid error for invalid instances" do
261
+ assert_raise(ActiveRecord::RecordInvalid) do
262
+ @factory.create(:first_name => nil)
263
+ end
264
+ end
265
+
266
+ context "when creating an instance with construct" do
267
+
268
+ setup do
269
+ @instance = @factory.construct
270
+ end
271
+
272
+ should_instantiate_class
273
+
274
+ should "save the instance" do
275
+ assert !@instance.new_record?
276
+ end
277
+
278
+ end
279
+
280
+ should "not raise an ActiveRecord::RecordInvalid error for invalid instances" do
281
+ assert_nothing_raised do
282
+ @factory.construct(:first_name => nil)
283
+ end
284
+ end
285
+
286
+ end
287
+
288
+ end
289
+
290
+ context "Factory class" do
291
+
292
+ setup do
293
+ @name = :user
294
+ @attrs = { :last_name => 'Override' }
295
+ @first_name = 'Johnny'
296
+ @last_name = 'Winter'
297
+ @class = User
298
+
299
+ Factory.define(@name) do |u|
300
+ u.first_name @first_name
301
+ u.last_name { @last_name }
302
+ u.email 'jwinter@guitar.org'
303
+ end
304
+
305
+ @factory = Factory.factories[@name]
306
+ end
307
+
308
+ [:build, :create, :attributes_for].each do |method|
309
+
310
+ should "delegate the #{method} method to the factory instance" do
311
+ @factory.expects(method).with(@attrs)
312
+ Factory.send(method, @name, @attrs)
313
+ end
314
+
315
+ should "raise an ArgumentError when #{method} is called with a nonexistant factory" do
316
+ assert_raise(ArgumentError) { Factory.send(method, :bogus) }
317
+ end
318
+
319
+ end
320
+
321
+ should "call the create method from the top-level Factory() method" do
322
+ @factory.expects(:create).with(@attrs)
323
+ Factory(@name, @attrs)
324
+ end
325
+
326
+ end
327
+
328
+ context "after defining a sequence" do
329
+
330
+ setup do
331
+ @sequence = mock('sequence')
332
+ @name = :test
333
+ @value = '1 2 5'
334
+
335
+ @sequence. stubs(:next).returns(@value)
336
+ Factory::Sequence.stubs(:new). returns(@sequence)
337
+
338
+ Factory.sequence(@name) {}
339
+ end
340
+
341
+ should "call next on the sequence when sent next" do
342
+ @sequence.expects(:next)
343
+
344
+ Factory.next(@name)
345
+ end
346
+
347
+ should "return the value from the sequence" do
348
+ assert_equal @value, Factory.next(@name)
349
+ end
350
+
351
+ end
352
+
353
+ end
@@ -0,0 +1,143 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class IntegrationTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ Factory.define :user do |f|
7
+ f.first_name 'Jimi'
8
+ f.last_name 'Hendrix'
9
+ f.admin false
10
+ f.email {|a| "#{a.first_name}.#{a.last_name}@example.com".downcase }
11
+ end
12
+
13
+ Factory.define :post do |f|
14
+ f.name 'Test Post'
15
+ f.author {|a| a.association(:user) }
16
+ end
17
+
18
+ Factory.define :admin, :class => User do |f|
19
+ f.first_name 'Ben'
20
+ f.last_name 'Stein'
21
+ f.admin true
22
+ f.email { Factory.next(:email) }
23
+ end
24
+
25
+ Factory.sequence :email do |n|
26
+ "somebody#{n}@example.com"
27
+ end
28
+ end
29
+
30
+ def teardown
31
+ Factory.send(:class_variable_get, "@@factories").clear
32
+ end
33
+
34
+ context "a generated attributes hash" do
35
+
36
+ setup do
37
+ @attrs = Factory.attributes_for(:user, :first_name => 'Bill')
38
+ end
39
+
40
+ should "assign all attributes" do
41
+ assert_equal [:admin, :email, :first_name, :last_name],
42
+ @attrs.keys.sort {|a, b| a.to_s <=> b.to_s }
43
+ end
44
+
45
+ should "correctly assign lazy, dependent attributes" do
46
+ assert_equal "bill.hendrix@example.com", @attrs[:email]
47
+ end
48
+
49
+ should "override attrbutes" do
50
+ assert_equal 'Bill', @attrs[:first_name]
51
+ end
52
+
53
+ should "not assign associations" do
54
+ assert_nil Factory.attributes_for(:post)[:author]
55
+ end
56
+
57
+ end
58
+
59
+ context "a built instance" do
60
+
61
+ setup do
62
+ @instance = Factory.build(:post)
63
+ end
64
+
65
+ should "not be saved" do
66
+ assert @instance.new_record?
67
+ end
68
+
69
+ should "assign associations" do
70
+ assert_kind_of User, @instance.author
71
+ end
72
+
73
+ should "not save associations" do
74
+ assert @instance.author.new_record?
75
+ end
76
+
77
+ end
78
+
79
+ context "a created instance" do
80
+
81
+ setup do
82
+ @instance = Factory.create(:post)
83
+ end
84
+
85
+ should "be saved" do
86
+ assert !@instance.new_record?
87
+ end
88
+
89
+ should "assign associations" do
90
+ assert_kind_of User, @instance.author
91
+ end
92
+
93
+ should "save associations" do
94
+ assert !@instance.author.new_record?
95
+ end
96
+
97
+ end
98
+
99
+ context "an instance generated by a factory with a custom class name" do
100
+
101
+ setup do
102
+ @instance = Factory.create(:admin)
103
+ end
104
+
105
+ should "use the correct class name" do
106
+ assert_kind_of User, @instance
107
+ end
108
+
109
+ should "use the correct factory definition" do
110
+ assert @instance.admin?
111
+ end
112
+
113
+ end
114
+
115
+ context "an attribute generated by a sequence" do
116
+
117
+ setup do
118
+ @email = Factory.attributes_for(:admin)[:email]
119
+ end
120
+
121
+ should "match the correct format" do
122
+ assert_match /^somebody\d+@example\.com$/, @email
123
+ end
124
+
125
+ context "after the attribute has already been generated once" do
126
+
127
+ setup do
128
+ @another_email = Factory.attributes_for(:admin)[:email]
129
+ end
130
+
131
+ should "match the correct format" do
132
+ assert_match /^somebody\d+@example\.com$/, @email
133
+ end
134
+
135
+ should "not be the same as the first generated value" do
136
+ assert_not_equal @email, @another_email
137
+ end
138
+
139
+ end
140
+
141
+ end
142
+
143
+ end
@@ -0,0 +1,32 @@
1
+ ActiveRecord::Base.establish_connection(
2
+ :adapter => 'sqlite3',
3
+ :database => File.join(File.dirname(__FILE__), 'test.db')
4
+ )
5
+
6
+ class CreateSchema < ActiveRecord::Migration
7
+ def self.up
8
+ create_table :users, :force => true do |t|
9
+ t.string :first_name
10
+ t.string :last_name
11
+ t.string :email
12
+ t.boolean :admin, :default => false
13
+ end
14
+
15
+ create_table :posts, :force => true do |t|
16
+ t.string :name
17
+ t.integer :author_id
18
+ end
19
+ end
20
+ end
21
+
22
+ CreateSchema.suppress_messages { CreateSchema.migrate(:up) }
23
+
24
+ class User < ActiveRecord::Base
25
+ validates_presence_of :first_name, :last_name, :email
26
+ has_many :posts, :foreign_key => 'author_id'
27
+ end
28
+
29
+ class Post < ActiveRecord::Base
30
+ validates_presence_of :name, :author_id
31
+ belongs_to :author, :class_name => 'User'
32
+ end
@@ -0,0 +1,29 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class SequenceTest < Test::Unit::TestCase
4
+
5
+ context "a sequence" do
6
+
7
+ setup do
8
+ @sequence = Factory::Sequence.new {|n| "=#{n}" }
9
+ end
10
+
11
+ should "start with a value of 1" do
12
+ assert_equal "=1", @sequence.next
13
+ end
14
+
15
+ context "after being called" do
16
+
17
+ setup do
18
+ @sequence.next
19
+ end
20
+
21
+ should "use the next value" do
22
+ assert_equal "=2", @sequence.next
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,10 @@
1
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
2
+ $: << File.join(File.dirname(__FILE__))
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'activerecord'
7
+ require 'factory_girl'
8
+ require 'shoulda'
9
+ require 'mocha'
10
+ require 'models'
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jonysk-factory_girl
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Joe Ferris
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-07-18 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "1.0"
23
+ version:
24
+ description: factory_girl provides a framework and DSL for defining and using factories - less error-prone, more explicit, and all-around easier to work with than fixtures.
25
+ email: jferris@thoughtbot.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README.textile
32
+ files:
33
+ - LICENSE
34
+ - Rakefile
35
+ - README.textile
36
+ - lib/factory_girl/attribute_proxy.rb
37
+ - lib/factory_girl/factory.rb
38
+ - lib/factory_girl/sequence.rb
39
+ - lib/factory_girl.rb
40
+ - test/attribute_proxy_test.rb
41
+ - test/factory_test.rb
42
+ - test/integration_test.rb
43
+ - test/models.rb
44
+ - test/sequence_test.rb
45
+ - test/test_helper.rb
46
+ has_rdoc: true
47
+ homepage:
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --line-numbers
51
+ - --inline-source
52
+ - --main
53
+ - README.textile
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.2.0
72
+ signing_key:
73
+ specification_version: 2
74
+ summary: factory_girl provides a framework and DSL for defining and using model instance factories.
75
+ test_files:
76
+ - test/attribute_proxy_test.rb
77
+ - test/factory_test.rb
78
+ - test/integration_test.rb
79
+ - test/sequence_test.rb