fakutori-san 0.1.0

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 @@
1
+ rdoc
data/.kick ADDED
@@ -0,0 +1,12 @@
1
+ recipe :ruby
2
+
3
+ process do |files|
4
+ Ruby.run_tests(files.take_and_map do |file|
5
+ case file
6
+ when %r{^lib/(.*).rb$}
7
+ "test/#{$1.gsub('/', '_')}_test.rb"
8
+ when 'test/test_helper.rb'
9
+ Dir.glob('test/*_test.rb')
10
+ end
11
+ end)
12
+ end
@@ -0,0 +1,22 @@
1
+ Copyright © 2009
2
+ Eloy Duran, Fingertips <eloy@fngtps.com>
3
+ Manfred Stienstra, Fingertips <manfred@fngtps.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,41 @@
1
+ = Fakutori-San
2
+
3
+ Fakutori-San is an instance factory for your tests. Factories in Fakutori-San
4
+ are plain Ruby classes. It uses a number of naming rules, not magic, to do
5
+ smart things. This means you can use class inheritance and other standard
6
+ Ruby practices to define your factories.
7
+
8
+ Although Fakutori-San was written to be used in Rails with ActiveRecord it
9
+ only assumes the save method to persist the object. If your objects also
10
+ persist themselves with the save method you're golden.
11
+
12
+ == Short example
13
+
14
+ Fakutori-San uses some smart assumptions about methods in your factory class
15
+ so you can easily define all types of situations for your model.
16
+
17
+ module FakutoriSan
18
+ class MemberFakutori < Fakutori
19
+ def valid_attrs
20
+ { 'name' => 'Eloy', 'email' => 'eloy@example.com', 'password' => 'secret' }
21
+ end
22
+
23
+ def invalid_attrs
24
+ { 'name' => '' }
25
+ end
26
+ end
27
+ end
28
+
29
+ After you've defined a factory for your model you can instantiate it, for
30
+ instance in the setup method of your test.
31
+
32
+ @valid_member = Fakutori(Member).create_one
33
+ @invalid_member = Fakutori(Member).create_one(:invalid)
34
+
35
+ Fakutori-San looks for a class called FakutoriSan::MemberFakutori to create a
36
+ Member instance. It also knows that it should use invalid_attrs when creating
37
+ an invalid member. Neat huh?
38
+
39
+ If you want to learn more about Fakutori-San, please check out the examples,
40
+ the tests, and the implementation. Note that the code is not that long so it's
41
+ not a chore.
@@ -0,0 +1,42 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the fakutori-san plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the fakutori-san plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'Fakutori-san'
19
+ rdoc.options << '--line-numbers' << '--inline-source' << '--charset=utf8'
20
+ rdoc.rdoc_files.include('README.rdoc')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ begin
25
+ require 'jeweler'
26
+ Jeweler::Tasks.new do |s|
27
+ s.name = "fakutori-san"
28
+ s.homepage = "http://github.com/Fingertips/fakutori-san"
29
+ s.email = "eloy.de.enige@gmail.com"
30
+ s.authors = ["Eloy Duran"]
31
+ s.summary = s.description = "FakutoriSan is a lean model factory plugin which uses vanilla Ruby to define the factories, allowing you to optimally use inheritance etc."
32
+ end
33
+ rescue LoadError
34
+ end
35
+
36
+ begin
37
+ require 'jewelry_portfolio/tasks'
38
+ JewelryPortfolio::Tasks.new do |p|
39
+ p.account = 'Fingertips'
40
+ end
41
+ rescue LoadError
42
+ end
data/TODO ADDED
@@ -0,0 +1,10 @@
1
+ * Add scenario blocks which allow you to add a description to a set of Fakutori calls
2
+
3
+ scenario 'Calendar with events in multiple venues' do
4
+ Fakutori(Event).create!(:venue => venues(:melkweg))
5
+ Fakutori(Event).create!(:venue => venues(:paradiso))
6
+ end
7
+
8
+ * Speed up definition of scenarios by somehow dumping the contents of a scenario
9
+ * Invalidate the scenario cache when the file in which it was defined changes
10
+ * Fakutori should circumvent attr_accessible / attr_protected so it's easier to initialize models the way you want them
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 0
@@ -0,0 +1,39 @@
1
+ require 'faker' # For more information about Faker, see http://faker.rubyforge.org
2
+
3
+ # Always define your factories in the FakutoriSan module so they don't mix
4
+ # interfere with the rest of your code and FakutoriSan can find them.
5
+ module FakutoriSan
6
+ # Factories always subclass from Fakutori
7
+ class Article < Fakutori
8
+ # When creating a new object, Fakutori-San will always use the valid_attrs method by default.
9
+ def valid_attrs
10
+ { :title => Faker::Lorem.words.join(' '), :body => Faker::Lorem.paragraphs.join("\n\n") }
11
+ end
12
+
13
+ def invalid_attrs
14
+ { :title => '', :body => '' }
15
+ end
16
+ end
17
+ end
18
+
19
+ # After defining a factory you can plan, build, or create objects
20
+
21
+ # The plan method returns attributes from the factory
22
+ article_atributes = Fakutori(Article).plan
23
+ # The build method uses attributes from factory to instantiate an object
24
+ article = Fakutori(Article).build
25
+ # The create method builds the object and saves it
26
+ article = Fakutori(Article).create
27
+
28
+ # The plan, build, and create methods do smart things with their arguments.
29
+
30
+ # Use a different set of attributes to build
31
+ article = Fakutori(Article).build(:invalid)
32
+ # Override default attributes with your own custom ones
33
+ article = Fakutori(Article).build(:title => 'Breaking Bad')
34
+ # Build three articles
35
+ articles = Fakutori(Article).build(3)
36
+ # Build three invalid articles
37
+ articles = Fakutori(Article).build(3, :invalid)
38
+ # Build three invalid articles with a specified body
39
+ articles = Fakutori(Article).build(3, :invalid, :body => "Hi, I'm invalid")
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{fakutori-san}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Eloy Duran"]
12
+ s.date = %q{2010-10-01}
13
+ s.description = %q{FakutoriSan is a lean model factory plugin which uses vanilla Ruby to define the factories, allowing you to optimally use inheritance etc.}
14
+ s.email = %q{eloy.de.enige@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc",
17
+ "TODO"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ ".kick",
22
+ "MIT-LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "TODO",
26
+ "VERSION.yml",
27
+ "examples/simple_factory.rb",
28
+ "fakutori-san.gemspec",
29
+ "lib/fakutori_san.rb",
30
+ "lib/fakutori_san/fakutori.rb",
31
+ "rails/init.rb",
32
+ "test/factories/foo_fakutori.rb",
33
+ "test/factories/member_fakutori.rb",
34
+ "test/fakutori_san_fakutori_test.rb",
35
+ "test/fakutori_san_test.rb",
36
+ "test/models/article.rb",
37
+ "test/models/member.rb",
38
+ "test/models/namespaced/article.rb",
39
+ "test/models/unrelated.rb",
40
+ "test/test_helper.rb"
41
+ ]
42
+ s.homepage = %q{http://github.com/Fingertips/fakutori-san}
43
+ s.rdoc_options = ["--charset=UTF-8"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = %q{1.3.7}
46
+ s.summary = %q{FakutoriSan is a lean model factory plugin which uses vanilla Ruby to define the factories, allowing you to optimally use inheritance etc.}
47
+ s.test_files = [
48
+ "test/factories/foo_fakutori.rb",
49
+ "test/factories/member_fakutori.rb",
50
+ "test/fakutori_san_fakutori_test.rb",
51
+ "test/fakutori_san_scenarios_test.rb",
52
+ "test/fakutori_san_test.rb",
53
+ "test/models/article.rb",
54
+ "test/models/member.rb",
55
+ "test/models/namespaced/article.rb",
56
+ "test/models/unrelated.rb",
57
+ "test/test_helper.rb",
58
+ "examples/simple_factory.rb"
59
+ ]
60
+
61
+ if s.respond_to? :specification_version then
62
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
63
+ s.specification_version = 3
64
+
65
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
66
+ else
67
+ end
68
+ else
69
+ end
70
+ end
71
+
@@ -0,0 +1,15 @@
1
+ require 'fakutori_san/fakutori'
2
+ require 'fakutori_san/scenarios'
3
+
4
+ # FakutoriSan is the module where most of the implementation resides.
5
+ module FakutoriSan
6
+ end
7
+
8
+ module Kernel
9
+ # The Fakutori method is used to instantiate your factories. For more information about defining
10
+ # and using factories see FakutoriSan::Fakutori and the examples.
11
+ def Fakutori(model)
12
+ FakutoriSan.factories[model] || raise(FakutoriSan::FakutoriMissing, "No factory defined for model `#{model}'")
13
+ end
14
+ private :Fakutori
15
+ end
@@ -0,0 +1,154 @@
1
+ module FakutoriSan
2
+ class FakutoriMissing < NameError; end
3
+
4
+ # Returns a hash of the available <tt>model => factory</tt> pairs.
5
+ def self.factories
6
+ @factories ||= {}
7
+ end
8
+
9
+ module FakutoriExt
10
+ def associate_to(model, options = nil)
11
+ @__factory__.associate(self, model, options)
12
+ self
13
+ end
14
+
15
+ def apply_scene(name, options = {})
16
+ @__factory__.scene(name, self, options)
17
+ end
18
+ end
19
+
20
+ class Collection < Array
21
+ include FakutoriExt
22
+
23
+ def initialize(factory, times)
24
+ @__factory__ = factory
25
+ super(times)
26
+ end
27
+
28
+ def factory
29
+ @__factory__
30
+ end
31
+ end
32
+
33
+ class Fakutori
34
+ class << self
35
+ def inherited(factory_klass)
36
+ model_klass = Object.const_get(factory_klass.name.gsub(/^FakutoriSan::|Fakutori$/, ''))
37
+ factory_klass.for_model model_klass
38
+ rescue NameError
39
+ end
40
+
41
+ def for_model(model)
42
+ FakutoriSan.factories[model] = new(model)
43
+ end
44
+ end
45
+
46
+ attr_reader :model
47
+
48
+ def initialize(model)
49
+ @model = model
50
+ end
51
+
52
+ def plan_one(*type_and_or_attributes)
53
+ attributes = type_and_or_attributes.extract_options!
54
+ type = type_and_or_attributes.pop || :valid
55
+ m = "#{type}_attrs"
56
+
57
+ if respond_to?(m)
58
+ plan = method(m).arity.zero? ? send(m) : send(m, attributes)
59
+ plan.merge(attributes)
60
+ else
61
+ raise NoMethodError, "#{self.class.name} has no attributes method for type `#{type}'"
62
+ end
63
+ end
64
+
65
+ def plan(*times_and_or_type_and_or_attributes)
66
+ multiple_times :plan, times_and_or_type_and_or_attributes
67
+ end
68
+
69
+ def build_one(*type_and_or_attributes)
70
+ make_chainable(@model.new(plan_one(*type_and_or_attributes)))
71
+ end
72
+
73
+ def build(*times_and_or_type_and_or_attributes)
74
+ multiple_times :build, times_and_or_type_and_or_attributes
75
+ end
76
+
77
+ def create_one(*type_and_or_attributes_and_or_validate)
78
+ args = type_and_or_attributes_and_or_validate
79
+
80
+ validate = args.pop if [true, false].include?(args.last)
81
+ instance = build_one(*args)
82
+ validate ? instance.save! : instance.save(false)
83
+ instance
84
+ end
85
+
86
+ def create_one!(*type_and_or_attributes)
87
+ type_and_or_attributes << true
88
+ create_one(*type_and_or_attributes)
89
+ end
90
+
91
+ def create(*times_and_or_type_and_or_attributes)
92
+ multiple_times :create, times_and_or_type_and_or_attributes
93
+ end
94
+
95
+ def create!(*times_and_or_type_and_or_attributes)
96
+ times_and_or_type_and_or_attributes << true
97
+ create(*times_and_or_type_and_or_attributes)
98
+ end
99
+
100
+ def associate(record_or_collection, to_model, options = nil)
101
+ if builder = association_builder_for(to_model)
102
+ [*record_or_collection].each do |record|
103
+ send(*[builder, record, to_model, options].compact)
104
+ end
105
+ else
106
+ raise NoMethodError, "#{self.class.name} has no association builder method for model `#{to_model.inspect}'."
107
+ end
108
+
109
+ record_or_collection
110
+ end
111
+
112
+ def scene(name, record_or_collection, options = {})
113
+ method = "#{name}_scene"
114
+ unless respond_to?(method)
115
+ raise NoMethodError, "#{self.class.name} has no scene method for scene `#{name.inspect}'"
116
+ end
117
+
118
+ if record_or_collection.is_a?(Array)
119
+ record_or_collection.each_with_index do |record, index|
120
+ options[:index] = index
121
+ send(method, record, options)
122
+ end
123
+ else
124
+ send(method, record_or_collection, options)
125
+ end
126
+
127
+ record_or_collection
128
+ end
129
+
130
+ private
131
+
132
+ def make_chainable(instance)
133
+ instance.extend(FakutoriExt)
134
+ instance.instance_variable_set(:@__factory__, self)
135
+ instance
136
+ end
137
+
138
+ def multiple_times(type, args)
139
+ m = "#{type}_one"
140
+
141
+ if args.first.is_a?(Numeric)
142
+ Collection.new(self, args.shift) { send(m, *args) }
143
+ else
144
+ send(m, *args)
145
+ end
146
+ end
147
+
148
+ def association_builder_for(model)
149
+ klass = model.is_a?(Class) ? model : model.class
150
+ name = "associate_to_#{klass.name.underscore.gsub('/', '_')}".to_sym
151
+ name if respond_to?(name)
152
+ end
153
+ end
154
+ end
@@ -0,0 +1 @@
1
+ require 'fakutori_san'
@@ -0,0 +1,5 @@
1
+ module FakutoriSan
2
+ class FooFakutori < Fakutori
3
+ for_model Article
4
+ end
5
+ end
@@ -0,0 +1,29 @@
1
+ module FakutoriSan
2
+ class MemberFakutori < Fakutori
3
+ def valid_attrs
4
+ { 'name' => 'Eloy', 'email' => 'eloy@example.com', 'password' => 'secret' }
5
+ end
6
+
7
+ def minimal_attrs
8
+ { 'name' => 'Eloy' }
9
+ end
10
+
11
+ def invalid_attrs
12
+ {}
13
+ end
14
+
15
+ def with_arg_attrs(arg)
16
+ { 'arg' => arg }
17
+ end
18
+
19
+ def with_name_scene(member, options)
20
+ member.update_attribute :name, "#{options[:name]}#{options[:index]}"
21
+ end
22
+
23
+ def associate_to_article(member, article, options)
24
+ end
25
+
26
+ def associate_to_namespaced_article(member, article, options)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,305 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ module SharedSpecsHelper
4
+ def define_shared_specs_for(type)
5
+ it "should call ##{type}_one multiple times and return an array of the resulting attribute hashes" do
6
+ attributes = { 'name' => 'Eloy' }
7
+
8
+ @factory.expects("#{type}_one").with(attributes).times(2).returns({})
9
+ @factory.send(type, 2, attributes).should == [{}, {}]
10
+
11
+ @factory.expects("#{type}_one").with(:minimal, attributes).times(2).returns({})
12
+ @factory.send(type, 2, :minimal, attributes).should == [{}, {}]
13
+ end
14
+
15
+ it "should not call ##{type}_one multiple times if no `times' argument is given" do
16
+ attributes = { 'name' => 'Eloy' }
17
+
18
+ @factory.expects("#{type}_one").with(attributes).times(1).returns({})
19
+ @factory.send(type, attributes).should == {}
20
+
21
+ @factory.expects("#{type}_one").with(:minimal, attributes).times(1).returns({})
22
+ @factory.send(type, :minimal, attributes).should == {}
23
+ end
24
+
25
+ it "should return a FakutoriSan::Collection instance when a collection is created" do
26
+ collection = @factory.send(type, 2)
27
+ collection.should.be.instance_of FakutoriSan::Collection
28
+ collection.factory.should.be @factory
29
+ end
30
+
31
+ unless type == :plan
32
+ it "should extend each instance returned by FakutoriSan with the FakutoriSan::FakutoriExt module" do
33
+ instance = @factory.create_one
34
+ FakutoriSan::FakutoriExt.instance_methods.each do |method|
35
+ instance.should.respond_to method
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ Test::Unit::TestCase.send(:extend, SharedSpecsHelper)
42
+
43
+ describe "FakutoriSan::Fakutori, concerning setup" do
44
+ it "should automatically find the model class based on the factory class's name and initialize an instance the factory subclass" do
45
+ factory = FakutoriSan.factories[Member]
46
+ factory.should.be.instance_of FakutoriSan::MemberFakutori
47
+ factory.model.should.be Member
48
+ end
49
+
50
+ it "should allow a user to explicitly define the model class when automatically finding the right class fails" do
51
+ factory = FakutoriSan.factories[Article]
52
+ factory.should.be.instance_of FakutoriSan::FooFakutori
53
+ factory.model.should.be Article
54
+ end
55
+ end
56
+
57
+ describe "The top level Fakutori method" do
58
+ it "should return the factory belonging to the given model" do
59
+ Fakutori(Member).should.be FakutoriSan.factories[Member]
60
+ Fakutori(Article).should.be FakutoriSan.factories[Article]
61
+ end
62
+
63
+ it "should raise a FakutoriSan::FakutoriMissing exception if no factory can be found" do
64
+ lambda {
65
+ Fakutori(Unrelated)
66
+ }.should.raise FakutoriSan::FakutoriMissing
67
+ end
68
+ end
69
+
70
+ describe "FakutoriSan::Fakutori, concerning `planning'" do
71
+ before do
72
+ @factory = Fakutori(Member)
73
+ end
74
+
75
+ define_shared_specs_for :plan
76
+
77
+ it "should return a hash of attributes" do
78
+ @factory.plan_one.should == { 'name' => 'Eloy', 'email' => 'eloy@example.com', 'password' => 'secret' }
79
+ end
80
+
81
+ it "should merge the given attributes onto the resulting attributes hash" do
82
+ @factory.plan_one('name' => 'Alloy', 'password' => 'supersecret').should ==
83
+ { 'name' => 'Alloy', 'email' => 'eloy@example.com', 'password' => 'supersecret' }
84
+ end
85
+
86
+ it "should pass the attributes hash to the `plan' method" do
87
+ @factory.plan_one(:with_arg, 'name' => 'Eloy').should ==
88
+ { 'name' => 'Eloy', 'arg' => { 'name' => 'Eloy' } }
89
+ end
90
+
91
+ it "should take an optional first plan `type', which invokes the method by the same name" do
92
+ @factory.plan_one(:minimal).should == { 'name' => 'Eloy' }
93
+ @factory.plan_one(:minimal, 'email' => 'eloy@example.com').should == { 'name' => 'Eloy', 'email' => 'eloy@example.com' }
94
+ end
95
+
96
+ it "should raise a NoMethodError if given a attributes type for which no method exists" do
97
+ lambda { @factory.plan_one(:unexisting) }.should.raise NoMethodError
98
+ end
99
+ end
100
+
101
+ describe "FakutoriSan::Fakutori, concerning `building'" do
102
+ before do
103
+ @factory = Fakutori(Member)
104
+ end
105
+
106
+ define_shared_specs_for :build
107
+
108
+ it "should build one instance with the default plan" do
109
+ instance = @factory.build_one
110
+ instance.should.be.new_record
111
+ instance.attributes.should == @factory.plan_one
112
+ end
113
+
114
+ it "should take an optional first plan `type', which invokes the method by the same name" do
115
+ instance = @factory.build_one(:minimal)
116
+ instance.should.be.new_record
117
+ instance.attributes.except('password', 'email').should == @factory.plan_one(:minimal)
118
+
119
+ instance = @factory.build_one(:minimal, 'email' => 'eloy@example.com')
120
+ instance.should.be.new_record
121
+ instance.attributes.except('password').should == @factory.plan_one(:minimal, 'email' => 'eloy@example.com')
122
+ end
123
+ end
124
+
125
+ describe "FakutoriSan::Fakutori, concerning `creating'" do
126
+ before do
127
+ @factory = Fakutori(Member)
128
+ end
129
+
130
+ define_shared_specs_for :create
131
+
132
+ it "should create one instance with the default plan" do
133
+ instance = @factory.create_one
134
+ instance.should.not.be.new_record
135
+ instance.attributes.except('id').should == @factory.plan_one
136
+ end
137
+
138
+ it "should not perform validations by default" do
139
+ instance = @factory.create_one(:invalid)
140
+ instance.should.not.be.new_record
141
+ instance.should.not.be.valid
142
+ end
143
+
144
+ it "should take an optional first plan `type', which invokes the method by the same name" do
145
+ instance = @factory.create_one(:minimal)
146
+ instance.should.not.be.new_record
147
+ instance.attributes.except('id', 'password', 'email').should == @factory.plan_one(:minimal)
148
+
149
+ instance = @factory.create_one(:minimal, 'email' => 'eloy@example.com')
150
+ instance.should.not.be.new_record
151
+ instance.attributes.except('id', 'password').should == @factory.plan_one(:minimal, 'email' => 'eloy@example.com')
152
+ end
153
+
154
+ it "should perform validations and raise an exception if created with #create_one!" do
155
+ lambda {
156
+ @factory.create_one!(:invalid)
157
+ }.should.raise ActiveRecord::RecordInvalid
158
+
159
+ lambda {
160
+ instance = @factory.create_one!(:minimal, 'password' => '12345')
161
+ instance.attributes.except('id', 'email').should == @factory.plan_one(:minimal, 'password' => '12345')
162
+ }.should.not.raise ActiveRecord::RecordInvalid
163
+ end
164
+
165
+ it "should call #create_one multiple times and perform validations, and return an array of the resulting record instances" do
166
+ attributes = { 'name' => 'Eloy' }
167
+
168
+ @factory.expects(:create_one).with(attributes, true).times(2).returns({})
169
+ @factory.create!(2, attributes)
170
+
171
+ @factory.expects(:create_one).with(:minimal, attributes, true).times(2).returns({})
172
+ @factory.create!(2, :minimal, attributes)
173
+ end
174
+ end
175
+
176
+ describe "FakutoriSan::Fakutori, concerning associating records" do
177
+ before do
178
+ @factory = Fakutori(Member)
179
+ @record = @factory.create_one
180
+ end
181
+
182
+ it "should return the association builder method if it exists for the given model" do
183
+ @factory.send(:association_builder_for, Article).should == :associate_to_article
184
+ @factory.send(:association_builder_for, Article.new).should == :associate_to_article
185
+
186
+ @factory.send(:association_builder_for, Namespaced::Article).should == :associate_to_namespaced_article
187
+ @factory.send(:association_builder_for, Namespaced::Article.new).should == :associate_to_namespaced_article
188
+ end
189
+
190
+ it "should return nil if no association builder method can be found for the given model" do
191
+ @factory.send(:association_builder_for, Unrelated).should == nil
192
+ @factory.send(:association_builder_for, Unrelated.new).should == nil
193
+ end
194
+
195
+ it "should call a builder method if it exists for the given model class" do
196
+ options = {}
197
+ @factory.expects(:associate_to_article).with(@record, Article, options)
198
+ @factory.associate(@record, Article, options).should.be @record
199
+ end
200
+
201
+ it "should call a builder method for each member of a collection" do
202
+ options = {}
203
+ collection = @factory.create(2)
204
+
205
+ @factory.expects(:associate_to_article).with(collection.first, Article, options)
206
+ @factory.expects(:associate_to_article).with(collection.last, Article, options)
207
+ @factory.associate(collection, Article, options).should.be collection
208
+ end
209
+
210
+ it "should raise an NoMethodError if an association builder method doesn't exist for a given model" do
211
+ lambda {
212
+ @factory.associate(@record, Unrelated, {})
213
+ }.should.raise NoMethodError
214
+ end
215
+
216
+ it "should only forward the options hash if it's given by the user" do
217
+ @factory.expects(:associate_to_article).with(@record, Article)
218
+ @factory.associate(@record, Article)
219
+ end
220
+ end
221
+
222
+ describe "FakutoriSan::Collection, concerning associating records" do
223
+ before do
224
+ @factory = Fakutori(Member)
225
+ @collection = @factory.create!(2)
226
+ end
227
+
228
+ it "should call #associate on each member and forward the given model and arguments" do
229
+ options = { 'name' => 'Eloy' }
230
+ @factory.expects(:associate).with(@collection, Article, options)
231
+ @collection.associate_to(Article, options)
232
+ end
233
+
234
+ it "should return itself after associating so the user can chain calls" do
235
+ @collection.associate_to(Article, {}).should.be @collection
236
+ end
237
+
238
+ it "should forward the options as `nil' by default" do
239
+ @factory.expects(:associate).with(@collection, Article, nil)
240
+ @collection.associate_to(Article)
241
+ end
242
+ end
243
+
244
+ describe "FakutoriSan::Fakutori, concerning `scenes'" do
245
+ before do
246
+ @factory = Fakutori(Member)
247
+ end
248
+
249
+ it "should invoke a scene method if it exists and return self" do
250
+ instance = @factory.create_one
251
+ @factory.scene(:with_name, instance, :name => 'Alloy').should == instance
252
+ instance.name.should == 'Alloy'
253
+ end
254
+
255
+ it "should invoke a scene method for each record in a collection, assign the index to the options, and return self" do
256
+ collection = @factory.create!(2)
257
+ @factory.scene(:with_name, collection, :name => 'Alloy').should == collection
258
+ collection.each_with_index do |record, index|
259
+ record.reload.name.should == "Alloy#{index}"
260
+ end
261
+ end
262
+
263
+ it "should raise a NoMethodError if a requested scene does not exist" do
264
+ lambda {
265
+ @factory.scene(:does_not_exist, @factory.build_one)
266
+ }.should.raise NoMethodError
267
+ end
268
+ end
269
+
270
+ describe "FakutoriSan::Collection, concerning `scenes'" do
271
+ before do
272
+ @factory = Fakutori(Member)
273
+ @collection = @factory.create!(2)
274
+ end
275
+
276
+ it "should call Fakutori#scene with the given scene name, itself, and options" do
277
+ @collection.apply_scene(:with_name, :name => 'Alloy')
278
+ @collection.each do |record|
279
+ record.reload.name.should.match /^Alloy/
280
+ end
281
+ end
282
+ end
283
+
284
+ describe "FakutoriSan::FakutoriExt" do
285
+ before do
286
+ @factory = Fakutori(Member)
287
+ end
288
+
289
+ it "should call Fakutori#associate with the record and options given and return itself" do
290
+ instance = @factory.create_one
291
+
292
+ @factory.expects(:associate).with(instance, Article, nil)
293
+ instance.associate_to(Article).should.be instance
294
+
295
+ options = {}
296
+ @factory.expects(:associate).with(instance, Article, options)
297
+ instance.associate_to(Article, options).should.be instance
298
+ end
299
+
300
+ it "should call Fakutori#scene with the record and options given" do
301
+ instance = @factory.create_one
302
+ instance.apply_scene(:with_name).reload.name.should == ''
303
+ instance.apply_scene(:with_name, :name => 'Alloy').reload.name.should == 'Alloy'
304
+ end
305
+ end
@@ -0,0 +1,61 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ [:uncached, :cached].each do |c|
4
+ describe "FakutoriSan scenarios, when #{c.to_s}" do
5
+ # before(:all) do
6
+ # p 'here'
7
+ # Member.delete_all
8
+ # @merel = Member.create!(:name => 'Merel')
9
+ # end
10
+
11
+ Fakutori.scenario(self, 'Manfred and Eloy') do
12
+ @manfred = Fakutori(Member).create(:name => 'Manfred')
13
+ @eloy = Fakutori(Member).create(:name => 'Eloy')
14
+ @thijs = Fakutori(Member).build(:name => 'Thijs')
15
+ @count = 3
16
+ end
17
+
18
+ # it "should define instance variables created in the scenario" do
19
+ # @count.should == 3
20
+ # @thijs.name.should == 'Thijs'
21
+ # end
22
+ #
23
+ # it "should find all records created" do
24
+ # Member.find_by_name('Manfred').should == @manfred
25
+ # Member.find_by_name('Eloy').should == @eloy
26
+ # Member.find_by_name('Thijs').should.be.nil
27
+ # Member.find_by_name('Merel').should.not.be.nil
28
+ # end
29
+
30
+ it "should have" do
31
+ p Member.all.map(&:name)
32
+ Member.find_by_name('Manfred').should.not.be.nil
33
+ end
34
+
35
+ it "should have 2" do
36
+ p Member.all.map(&:name)
37
+ Member.find_by_name('Manfred').should.not.be.nil
38
+ end
39
+ end
40
+ end
41
+
42
+ # describe "FakutoriSan::Scenario" do
43
+ # before do
44
+ # @scenario_1 = FakutoriSan::Scenario.new(__FILE__, self, "scenario 1") { @a = 1 }
45
+ # @scenario_2 = FakutoriSan::Scenario.new(__FILE__, self, "scenario 2") { @b = 2 }
46
+ # end
47
+ #
48
+ # it "should return a hash specific to the scenario" do
49
+ # @scenario_1.hash.should == @scenario_1.hash
50
+ # @scenario_1.hash.should.not == @scenario_2.hash
51
+ # end
52
+ #
53
+ # it "should return a dump directory specific for the scenario" do
54
+ # @scenario_1.dump_dir.should == @scenario_1.dump_dir
55
+ # @scenario_1.dump_dir.should.not == @scenario_2.dump_dir
56
+ # end
57
+ #
58
+ # it "should know the tables to cache" do
59
+ # @scenario_1.tables.sort.should == %w(members articles).sort
60
+ # end
61
+ # end
@@ -0,0 +1,8 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe "FakutoriSan" do
4
+ it "should define a method on kernel to create factories" do
5
+ fakutori = Fakutori(Member)
6
+ fakutori.model.should == Member
7
+ end
8
+ end
@@ -0,0 +1,2 @@
1
+ class Article < ActiveRecord::Base
2
+ end
@@ -0,0 +1,3 @@
1
+ class Member < ActiveRecord::Base
2
+ validates_presence_of :name
3
+ end
@@ -0,0 +1,4 @@
1
+ module Namespaced
2
+ class Article
3
+ end
4
+ end
@@ -0,0 +1,2 @@
1
+ class Unrelated
2
+ end
@@ -0,0 +1,59 @@
1
+ TEST_ROOT_DIR = File.expand_path('..', __FILE__)
2
+ require 'test/unit'
3
+
4
+ frameworks = {
5
+ 'activesupport' => %w(active_support),
6
+ 'activerecord' => %w(active_record),
7
+ 'actionpack' => %w(action_controller)
8
+ }
9
+
10
+ rails = [
11
+ File.expand_path('../../../rails', TEST_ROOT_DIR),
12
+ File.expand_path('../../rails', TEST_ROOT_DIR)
13
+ ].detect do |possible_rails|
14
+ begin
15
+ entries = Dir.entries(possible_rails)
16
+ frameworks.keys.all? { |framework| entries.include?(framework) }
17
+ rescue Errno::ENOENT
18
+ false
19
+ end
20
+ end
21
+ frameworks.keys.each { |framework| $:.unshift(File.join(rails, framework, 'lib')) }
22
+
23
+ $:.unshift File.join(TEST_ROOT_DIR, '/../lib')
24
+ $:.unshift File.join(TEST_ROOT_DIR, '/lib')
25
+ $:.unshift TEST_ROOT_DIR
26
+
27
+ ENV['RAILS_ENV'] = 'test'
28
+
29
+ # Require Rails components
30
+ frameworks.values.flatten.each { |lib| require lib }
31
+ require File.expand_path('../../rails/init', __FILE__)
32
+
33
+ # Libraries for testing
34
+ require 'rubygems' rescue LoadError
35
+ require 'test/spec'
36
+ require 'mocha'
37
+
38
+ # Open a connection for ActiveRecord
39
+ ActiveRecord::Base.establish_connection(:adapter => "mysql", :database => "fakutori_san_test")
40
+ ActiveRecord::Migration.verbose = false
41
+ ActiveRecord::Schema.define(:version => 1) do
42
+ create_table :members, :force => true do |t|
43
+ t.string :name
44
+ t.string :email
45
+ t.string :password
46
+ end
47
+
48
+ create_table :articles, :force => true do |t|
49
+ end
50
+ end
51
+
52
+ # Require all models and factories used in the tests
53
+ Dir.glob(File.join(TEST_ROOT_DIR, 'models', '**', '*.rb')).each do |model|
54
+ require model
55
+ end
56
+
57
+ Dir.glob(File.join(TEST_ROOT_DIR, 'factories', '**', '*.rb')).each do |factory|
58
+ require factory
59
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fakutori-san
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Eloy Duran
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-01 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: FakutoriSan is a lean model factory plugin which uses vanilla Ruby to define the factories, allowing you to optimally use inheritance etc.
23
+ email: eloy.de.enige@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.rdoc
30
+ - TODO
31
+ files:
32
+ - .gitignore
33
+ - .kick
34
+ - MIT-LICENSE
35
+ - README.rdoc
36
+ - Rakefile
37
+ - TODO
38
+ - VERSION.yml
39
+ - examples/simple_factory.rb
40
+ - fakutori-san.gemspec
41
+ - lib/fakutori_san.rb
42
+ - lib/fakutori_san/fakutori.rb
43
+ - rails/init.rb
44
+ - test/factories/foo_fakutori.rb
45
+ - test/factories/member_fakutori.rb
46
+ - test/fakutori_san_fakutori_test.rb
47
+ - test/fakutori_san_test.rb
48
+ - test/models/article.rb
49
+ - test/models/member.rb
50
+ - test/models/namespaced/article.rb
51
+ - test/models/unrelated.rb
52
+ - test/test_helper.rb
53
+ - test/fakutori_san_scenarios_test.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/Fingertips/fakutori-san
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --charset=UTF-8
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.3.7
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: FakutoriSan is a lean model factory plugin which uses vanilla Ruby to define the factories, allowing you to optimally use inheritance etc.
88
+ test_files:
89
+ - test/factories/foo_fakutori.rb
90
+ - test/factories/member_fakutori.rb
91
+ - test/fakutori_san_fakutori_test.rb
92
+ - test/fakutori_san_scenarios_test.rb
93
+ - test/fakutori_san_test.rb
94
+ - test/models/article.rb
95
+ - test/models/member.rb
96
+ - test/models/namespaced/article.rb
97
+ - test/models/unrelated.rb
98
+ - test/test_helper.rb
99
+ - examples/simple_factory.rb