ryanb-factory_girl 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ 1.1.2 (June 28, 2008)
2
+ Removing activespport dependency so there's no conflict with edge rails.
3
+
4
+ 1.1.1 (June 23, 2008)
5
+ The attribute "name" no longer requires using #add_attribute
6
+
7
+ 1.1.0 (June 3, 2008)
8
+ Added support for dependent attributes
9
+ Fixed the attributes_for build strategy to not build associations
10
+ Added support for sequences
11
+
12
+ 1.0.0 (May 31, 208)
13
+ First version
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,125 @@
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, 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 but does not save a User and a Post
87
+ Factory.build(:post)
88
+ post.new_record? # => true
89
+ post.author.new_record # => true</code></pre>
90
+
91
+
92
+ h2. Sequences
93
+
94
+ Unique values in a specific format (for example, e-mail addresses) can be
95
+ generated using sequences. Sequences are defined by calling Factory.sequence,
96
+ and values in a sequence are generated by calling Factory.next:
97
+
98
+ <pre><code># Defines a new sequence
99
+ Factory.sequence :email do |n|
100
+ "person#{n}@example.com"
101
+ end
102
+
103
+ Factory.next :email
104
+ # => "person1@example.com"
105
+
106
+ Factory.next :email
107
+ # => "person2@example.com"</code></pre>
108
+
109
+
110
+ h2. Using factories
111
+
112
+ <pre><code># Build and save a User instance
113
+ Factory(:user)
114
+
115
+ # Build a User instance and override the first_name property
116
+ Factory.build(:user, :first_name => 'Joe')
117
+
118
+ # Return an attributes Hash that can be used to build a User instance
119
+ attrs = Factory.attributes_for(:user)</code></pre>
120
+
121
+ h2. More Information
122
+
123
+ "Our blog":http://giantrobots.thoughtbot.com
124
+
125
+ "factory_girl rdoc":http://dev.thoughtbot.com/factory_girl
@@ -0,0 +1,68 @@
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
+ end
54
+
55
+ Rake::GemPackageTask.new spec do |pkg|
56
+ pkg.need_tar = true
57
+ pkg.need_zip = true
58
+ end
59
+
60
+ desc "Clean files generated by rake tasks"
61
+ task :clobber => [:clobber_rdoc, :clobber_package]
62
+
63
+ desc "Generate a gemspec file"
64
+ task :gemspec do
65
+ File.open("#{spec.name}.gemspec", 'w') do |f|
66
+ f.write spec.to_ruby
67
+ end
68
+ 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,216 @@
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
+ class << self
140
+
141
+ # Generates and returns a Hash of attributes from this factory. Attributes
142
+ # can be individually overridden by passing in a Hash of attribute => value
143
+ # pairs.
144
+ #
145
+ # Arguments:
146
+ # attrs: (Hash)
147
+ # Attributes to overwrite for this set.
148
+ #
149
+ # Returns:
150
+ # A set of attributes that can be used to build an instance of the class
151
+ # this factory generates. (Hash)
152
+ def attributes_for (name, attrs = {})
153
+ factory_by_name(name).attributes_for(attrs)
154
+ end
155
+
156
+ # Generates and returns an instance from this factory. Attributes can be
157
+ # individually overridden by passing in a Hash of attribute => value pairs.
158
+ #
159
+ # Arguments:
160
+ # attrs: (Hash)
161
+ # See attributes_for
162
+ #
163
+ # Returns:
164
+ # An instance of the class this factory generates, with generated
165
+ # attributes assigned.
166
+ def build (name, attrs = {})
167
+ factory_by_name(name).build(attrs)
168
+ end
169
+
170
+ # Generates, saves, and returns an instance from this factory. Attributes can
171
+ # be individually overridden by passing in a Hash of attribute => value
172
+ # pairs.
173
+ #
174
+ # If the instance is not valid, an ActiveRecord::Invalid exception will be
175
+ # raised.
176
+ #
177
+ # Arguments:
178
+ # attrs: (Hash)
179
+ # See attributes_for
180
+ #
181
+ # Returns:
182
+ # A saved instance of the class this factory generates, with generated
183
+ # attributes assigned.
184
+ def create (name, attrs = {})
185
+ factory_by_name(name).create(attrs)
186
+ end
187
+
188
+ private
189
+
190
+ def factory_by_name (name)
191
+ factories[name] or raise ArgumentError.new("No such factory: #{name.inspect}")
192
+ end
193
+
194
+ end
195
+
196
+ private
197
+
198
+ def build_attributes_hash (override, strategy)
199
+ result = @static_attributes.merge(override)
200
+ @lazy_attribute_names.each do |name|
201
+ proxy = AttributeProxy.new(self, name, strategy, result)
202
+ result[name] = @lazy_attribute_blocks[name].call(proxy) unless override.key?(name)
203
+ end
204
+ result
205
+ end
206
+
207
+ def build_instance (override, strategy)
208
+ instance = build_class.new
209
+ attrs = build_attributes_hash(override, strategy)
210
+ attrs.each do |attr, value|
211
+ instance.send(:"#{attr}=", value)
212
+ end
213
+ instance
214
+ end
215
+
216
+ 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,333 @@
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
+ end
267
+
268
+ end
269
+
270
+ context "Factory class" do
271
+
272
+ setup do
273
+ @name = :user
274
+ @attrs = { :last_name => 'Override' }
275
+ @first_name = 'Johnny'
276
+ @last_name = 'Winter'
277
+ @class = User
278
+
279
+ Factory.define(@name) do |u|
280
+ u.first_name @first_name
281
+ u.last_name { @last_name }
282
+ u.email 'jwinter@guitar.org'
283
+ end
284
+
285
+ @factory = Factory.factories[@name]
286
+ end
287
+
288
+ [:build, :create, :attributes_for].each do |method|
289
+
290
+ should "delegate the #{method} method to the factory instance" do
291
+ @factory.expects(method).with(@attrs)
292
+ Factory.send(method, @name, @attrs)
293
+ end
294
+
295
+ should "raise an ArgumentError when #{method} is called with a nonexistant factory" do
296
+ assert_raise(ArgumentError) { Factory.send(method, :bogus) }
297
+ end
298
+
299
+ end
300
+
301
+ should "call the create method from the top-level Factory() method" do
302
+ @factory.expects(:create).with(@attrs)
303
+ Factory(@name, @attrs)
304
+ end
305
+
306
+ end
307
+
308
+ context "after defining a sequence" do
309
+
310
+ setup do
311
+ @sequence = mock('sequence')
312
+ @name = :test
313
+ @value = '1 2 5'
314
+
315
+ @sequence. stubs(:next).returns(@value)
316
+ Factory::Sequence.stubs(:new). returns(@sequence)
317
+
318
+ Factory.sequence(@name) {}
319
+ end
320
+
321
+ should "call next on the sequence when sent next" do
322
+ @sequence.expects(:next)
323
+
324
+ Factory.next(@name)
325
+ end
326
+
327
+ should "return the value from the sequence" do
328
+ assert_equal @value, Factory.next(@name)
329
+ end
330
+
331
+ end
332
+
333
+ 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.title '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 :title
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 :title, :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,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ryanb-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-06-28 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ 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.
17
+ email: jferris@thoughtbot.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.textile
24
+ files:
25
+ - Changelog
26
+ - LICENSE
27
+ - Rakefile
28
+ - README.textile
29
+ - lib/factory_girl/attribute_proxy.rb
30
+ - lib/factory_girl/factory.rb
31
+ - lib/factory_girl/sequence.rb
32
+ - lib/factory_girl.rb
33
+ - test/attribute_proxy_test.rb
34
+ - test/factory_test.rb
35
+ - test/integration_test.rb
36
+ - test/models.rb
37
+ - test/sequence_test.rb
38
+ - test/test_helper.rb
39
+ has_rdoc: true
40
+ homepage:
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --line-numbers
44
+ - --inline-source
45
+ - --main
46
+ - README.textile
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.2.0
65
+ signing_key:
66
+ specification_version: 2
67
+ summary: factory_girl provides a framework and DSL for defining and using model instance factories.
68
+ test_files:
69
+ - test/attribute_proxy_test.rb
70
+ - test/factory_test.rb
71
+ - test/integration_test.rb
72
+ - test/sequence_test.rb