fakutori-san 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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