cranky 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +10 -17
- data/init.rb +1 -1
- data/lib/cranky/factory.rb +99 -0
- data/lib/cranky/job.rb +44 -0
- data/lib/cranky/version.rb +3 -0
- data/lib/cranky.rb +15 -103
- data/spec/cranky_spec.rb +27 -27
- data/spec/spec_helper.rb +31 -10
- metadata +8 -5
data/README.rdoc
CHANGED
@@ -29,14 +29,16 @@ You can create as many different factory files as you want, just require them in
|
|
29
29
|
|
30
30
|
== In a Nutshell
|
31
31
|
|
32
|
-
Cranky steals its core syntax from Factory Girl...
|
32
|
+
Cranky steals its core syntax from Factory Girl and can drop into tests already written for that framework...
|
33
33
|
|
34
34
|
Factory.build(:user) # Build a user instance without saving
|
35
35
|
Factory.create(:user) # Build and save a user instance
|
36
36
|
Factory.build(:user, :name => "Ian") # Override a default attribute value
|
37
37
|
Factory.attributes_for(:user) # Return a set of valid attributes rather than the object
|
38
38
|
|
39
|
-
|
39
|
+
Or if you're coming from Machinist, you can make your Cranky factories drop into your existing tests by setting up make and make! methods as shown here.[http://gist.github.com/525653]
|
40
|
+
|
41
|
+
Cranky has a nice debug option (rails only) to warn you when your factory is broken, recommend you do this for your first spec...
|
40
42
|
|
41
43
|
describe User do
|
42
44
|
it "should have a working factory" do
|
@@ -48,7 +50,7 @@ And has a nice debug option (rails only) to warn you when your factory is broken
|
|
48
50
|
Cranky allows you to build factories via std Ruby methods, like this...
|
49
51
|
|
50
52
|
# factories/my_factories.rb
|
51
|
-
class Cranky
|
53
|
+
class Cranky::Factory # Your factory must extend Cranky::Factory
|
52
54
|
|
53
55
|
# Simple factory method to create a user instance, you would call this via Factory.build(:user)
|
54
56
|
def user
|
@@ -87,14 +89,14 @@ This is where Cranky really shines, if you can create Ruby methods you can prett
|
|
87
89
|
|
88
90
|
The only rules are:
|
89
91
|
|
90
|
-
1. Your factory must
|
92
|
+
1. Your factory must extend the Cranky::Factory class
|
91
93
|
2. Your factory method must return the object you wanted to create
|
92
94
|
3. You can access the overrides passed in via options[:key] (not really a rule!)
|
93
95
|
|
94
96
|
So for example to create a simple user factory...
|
95
97
|
|
96
98
|
# factories/my_factories.rb
|
97
|
-
class Cranky
|
99
|
+
class Cranky::Factory
|
98
100
|
|
99
101
|
# Simple factory method to create a user instance, you would call this via Factory.build(:user)
|
100
102
|
def user
|
@@ -112,7 +114,7 @@ Now of course you are working in straight Ruby here, so you can extend this any
|
|
112
114
|
For example here it is with the capability to automatically create a default address association...
|
113
115
|
|
114
116
|
# factories/my_factories.rb
|
115
|
-
class Cranky
|
117
|
+
class Cranky::Factory
|
116
118
|
|
117
119
|
# Return the default address if it already exists, or call the address factory to make one
|
118
120
|
def default_address
|
@@ -233,17 +235,8 @@ Clear all instance variables in the factory. This may be useful to run between t
|
|
233
235
|
|
234
236
|
Sometimes it is useful to be warned that your factory is generating invalid instances (although quite often your tests may intentionally generate invalid instances, so use this with care). By turning on debug the Factory will raise an error if the generated instance is invalid...
|
235
237
|
|
236
|
-
Factory.debug
|
237
|
-
|
238
|
-
Or run within a block...
|
239
|
-
|
240
|
-
Factory.debug do
|
241
|
-
Factory.build(:user)
|
242
|
-
end
|
243
|
-
|
244
|
-
Or inline (runs the build method with debug enabled)...
|
245
|
-
|
246
|
-
Factory.debug(:user)
|
238
|
+
Factory.debug(:user) # A replacement for Factory.build, with validation warnings enabled
|
239
|
+
Factory.debug!(:user) # Likewise for Factory.create
|
247
240
|
|
248
241
|
Note that this relies on the instance having a valid? method, so in practice this may only work with Rails.
|
249
242
|
|
data/init.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require '
|
1
|
+
require 'cranky'
|
2
2
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Cranky
|
2
|
+
class Factory
|
3
|
+
|
4
|
+
attr_writer :debug
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
# Factory jobs can be nested, i.e. a factory method can itself invoke another factory method to
|
8
|
+
# build a dependent object. In this case jobs the jobs are pushed into a pipeline and executed
|
9
|
+
# in a last in first out order.
|
10
|
+
@pipeline = []
|
11
|
+
@n = 0
|
12
|
+
@errors = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def build(what, overrides={})
|
16
|
+
crank_it(what, overrides)
|
17
|
+
end
|
18
|
+
|
19
|
+
def create(what, overrides={})
|
20
|
+
item = build(what, overrides)
|
21
|
+
item.save
|
22
|
+
item
|
23
|
+
end
|
24
|
+
|
25
|
+
# Reset the factory instance, clear all instance variables
|
26
|
+
def reset
|
27
|
+
self.instance_variables.each do |var|
|
28
|
+
instance_variable_set(var, nil)
|
29
|
+
end
|
30
|
+
initialize
|
31
|
+
end
|
32
|
+
|
33
|
+
def attributes_for(what, attrs={})
|
34
|
+
build(what, attrs).attributes
|
35
|
+
end
|
36
|
+
|
37
|
+
# Can be left in your tests as an alternative to build and to warn if your factory method
|
38
|
+
# ever starts producing invalid instances
|
39
|
+
def debug(*args)
|
40
|
+
item = build(*args)
|
41
|
+
if !item.valid?
|
42
|
+
raise "Oops, the #{item.class} created by the Factory has the following errors: #{item.errors}"
|
43
|
+
end
|
44
|
+
item
|
45
|
+
end
|
46
|
+
|
47
|
+
# Same thing for create
|
48
|
+
def debug!(*args)
|
49
|
+
item = debug(*args)
|
50
|
+
item.save
|
51
|
+
item
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def n
|
57
|
+
@n += 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def inherit(what, overrides={})
|
61
|
+
build(what, overrides.merge(options))
|
62
|
+
end
|
63
|
+
|
64
|
+
# Execute the requested factory method, crank out the target object!
|
65
|
+
def crank_it(what, overrides)
|
66
|
+
item = "TBD"
|
67
|
+
new_job(what, overrides) do
|
68
|
+
item = self.send(what) # Invoke the factory method
|
69
|
+
end
|
70
|
+
item
|
71
|
+
end
|
72
|
+
|
73
|
+
# This method actually makes the required object instance, it gets called by the users factory
|
74
|
+
# method, where the name 'define' makes more sense than it does here!
|
75
|
+
def define(defaults={})
|
76
|
+
current_job.defaults = defaults
|
77
|
+
current_job.execute
|
78
|
+
end
|
79
|
+
|
80
|
+
def current_job
|
81
|
+
@pipeline.last
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns a hash containing any top-level overrides passed in when the current factory was invoked
|
85
|
+
def options
|
86
|
+
current_job.overrides
|
87
|
+
end
|
88
|
+
|
89
|
+
# Adds a new job to the pipeline then yields to the caller to execute it
|
90
|
+
def new_job(what, overrides)
|
91
|
+
@pipeline << Job.new(what, overrides)
|
92
|
+
yield
|
93
|
+
@pipeline.pop
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
data/lib/cranky/job.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Cranky
|
2
|
+
class Job
|
3
|
+
|
4
|
+
attr_writer :defaults
|
5
|
+
attr_reader :overrides
|
6
|
+
|
7
|
+
def initialize(target, overrides={})
|
8
|
+
@defaults = {}
|
9
|
+
@target = target
|
10
|
+
@overrides = overrides
|
11
|
+
end
|
12
|
+
|
13
|
+
def attributes
|
14
|
+
@attributes ||= @defaults.merge(@overrides)
|
15
|
+
end
|
16
|
+
|
17
|
+
def defaults=(defs)
|
18
|
+
@attributes = nil # Needs to be re-calculated
|
19
|
+
@defaults = defs
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute
|
23
|
+
item = get_constant(attributes[:class] ? attributes[:class] : @target).new
|
24
|
+
# Assign all explicit attributes first
|
25
|
+
attributes.each do |attribute, value|
|
26
|
+
item.send("#{attribute}=", value) if item.respond_to?("#{attribute}=") && !value.respond_to?("call")
|
27
|
+
end
|
28
|
+
# Then call any blocks
|
29
|
+
attributes.each do |attribute, value|
|
30
|
+
item.send("#{attribute}=", value.call(item)) if item.respond_to?("#{attribute}=") && value.respond_to?("call")
|
31
|
+
end
|
32
|
+
item
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Nicked from here: http://gist.github.com/301173
|
38
|
+
def get_constant(name_sym)
|
39
|
+
name = name_sym.to_s.split('_').collect {|s| s.capitalize }.join('')
|
40
|
+
Object.const_defined?(name) ? Object.const_get(name) : Object.const_missing(name)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/cranky.rb
CHANGED
@@ -1,106 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
# end
|
14
|
-
|
15
|
-
attr_writer :debug
|
16
|
-
|
17
|
-
def initialize
|
18
|
-
@what = []
|
19
|
-
@attrs = []
|
20
|
-
@n = 0
|
21
|
-
end
|
22
|
-
|
23
|
-
def build(what, attrs={})
|
24
|
-
crank_it(what, false, attrs)
|
25
|
-
end
|
26
|
-
|
27
|
-
def create(what, attrs={})
|
28
|
-
crank_it(what, true, attrs)
|
29
|
-
end
|
30
|
-
|
31
|
-
def reset
|
32
|
-
self.instance_variables.each do |var|
|
33
|
-
instance_variable_set(var, nil)
|
34
|
-
end
|
35
|
-
initialize
|
36
|
-
end
|
37
|
-
|
38
|
-
def attributes_for(what, attrs={})
|
39
|
-
build(what, attrs).attributes
|
40
|
-
end
|
41
|
-
|
42
|
-
def debug(what=nil)
|
43
|
-
if block_given?
|
44
|
-
@debug = true
|
45
|
-
yield
|
46
|
-
@debug = false
|
47
|
-
elsif what
|
48
|
-
@debug = true
|
49
|
-
item = build(what)
|
50
|
-
@debug = false
|
51
|
-
item
|
52
|
-
else
|
53
|
-
@debug
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def n
|
60
|
-
@n += 1
|
61
|
-
end
|
62
|
-
|
63
|
-
def inherit(what, attrs={})
|
64
|
-
build(what, attrs.merge(options))
|
65
|
-
end
|
66
|
-
|
67
|
-
def crank_it(what, save, attrs)
|
68
|
-
@attrs << attrs; @what << what
|
69
|
-
item = self.send(what)
|
70
|
-
@attrs.pop; @what.pop
|
71
|
-
if @debug && !item.valid?
|
72
|
-
raise "Oops, the #{what} created by the Factory has the following errors: #{item.errors}"
|
73
|
-
end
|
74
|
-
item.save if save
|
75
|
-
item
|
76
|
-
end
|
77
|
-
|
78
|
-
def define(attrs={})
|
79
|
-
final_attrs = attrs.merge(@attrs.last)
|
80
|
-
item = get_constant(attrs[:class] ? attrs[:class] : @what.last).new
|
81
|
-
final_attrs.delete(:class)
|
82
|
-
# Assign all explicit attributes first
|
83
|
-
final_attrs.each do |attr, value|
|
84
|
-
item.send("#{attr}=", value) if item.respond_to?("#{attr}=") && !value.respond_to?("call")
|
85
|
-
end
|
86
|
-
# Then call any blocks
|
87
|
-
final_attrs.each do |attr, value|
|
88
|
-
item.send("#{attr}=", value.call(item)) if item.respond_to?("#{attr}=") && value.respond_to?("call")
|
89
|
-
end
|
90
|
-
item
|
91
|
-
end
|
92
|
-
|
93
|
-
# Nicked from here: http://gist.github.com/301173
|
94
|
-
def get_constant(name_sym)
|
95
|
-
name = name_sym.to_s.split('_').collect {|s| s.capitalize }.join('')
|
96
|
-
Object.const_defined?(name) ? Object.const_get(name) : Object.const_missing(name)
|
97
|
-
end
|
98
|
-
|
99
|
-
def options
|
100
|
-
@attrs.last
|
101
|
-
end
|
102
|
-
|
1
|
+
require 'cranky/version'
|
2
|
+
require 'cranky/job'
|
3
|
+
require 'cranky/factory'
|
4
|
+
|
5
|
+
# Instantiate a factory, this enables an easy drop in for tests written for Factory Girl
|
6
|
+
Factory = Cranky::Factory.new unless defined?(Factory)
|
7
|
+
|
8
|
+
# Alternative Cranky specific syntax:
|
9
|
+
# crank(:user) # equivalent to Factory.build(:user)
|
10
|
+
# crank!(:user) # equivalent to Factory.create(:user)
|
11
|
+
def crank(*args)
|
12
|
+
Factory.build(*args)
|
103
13
|
end
|
104
14
|
|
105
|
-
|
15
|
+
def crank!(*args)
|
16
|
+
Factory.create(*args)
|
17
|
+
end
|
106
18
|
|
data/spec/cranky_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe Cranky do
|
3
|
+
describe "The Cranky factory" do
|
4
4
|
|
5
5
|
before(:each) do
|
6
6
|
end
|
@@ -54,6 +54,13 @@ describe Cranky do
|
|
54
54
|
b.role.should == :admin
|
55
55
|
end
|
56
56
|
|
57
|
+
it "should give top priority to attributes defined at the top level, even when inheriting" do
|
58
|
+
a = Factory.build(:admin_manually, :role => :something_else)
|
59
|
+
a.role.should == :something_else
|
60
|
+
b = Factory.build(:admin_by_define, :role => :something_else)
|
61
|
+
b.role.should == :something_else
|
62
|
+
end
|
63
|
+
|
57
64
|
it "should create unique values using the n method" do
|
58
65
|
a = Factory.build(:user)
|
59
66
|
b = Factory.build(:user)
|
@@ -66,40 +73,19 @@ describe Cranky do
|
|
66
73
|
describe "debugger" do
|
67
74
|
|
68
75
|
it "should raise an error if the factory produces an invalid object when enabled (rails only)" do
|
69
|
-
Factory.debug = true
|
70
76
|
error = false
|
71
77
|
begin
|
72
|
-
Factory.
|
78
|
+
Factory.debug(:user, :valid => false)
|
73
79
|
rescue
|
74
80
|
error = true
|
75
81
|
end
|
76
82
|
error.should == true
|
77
|
-
Factory.debug = false
|
78
83
|
end
|
79
84
|
|
80
|
-
it "
|
81
|
-
Factory.debug.should ==
|
82
|
-
|
83
|
-
Factory.debug
|
84
|
-
begin
|
85
|
-
Factory.build(:user)
|
86
|
-
rescue
|
87
|
-
error = true
|
88
|
-
end
|
89
|
-
end
|
90
|
-
error.should == true
|
91
|
-
Factory.debug.should == false
|
92
|
-
end
|
93
|
-
|
94
|
-
it "can be run inline" do
|
95
|
-
Factory.debug.should == false
|
96
|
-
error = false
|
97
|
-
begin
|
98
|
-
Factory.debug(:user)
|
99
|
-
rescue
|
100
|
-
error = true
|
101
|
-
end
|
102
|
-
error.should == true
|
85
|
+
it "should have debug work like build and create when there are no errors" do
|
86
|
+
Factory.debug(:user).class.should == User
|
87
|
+
Factory.debug(:user).saved?.should == false
|
88
|
+
Factory.debug!(:user).saved?.should == true
|
103
89
|
end
|
104
90
|
|
105
91
|
end
|
@@ -115,6 +101,20 @@ describe Cranky do
|
|
115
101
|
Factory.build(:user, :name => "jenny", :email => Proc.new{ |u| "#{u.name}@home.com" }).email.should == "jenny@home.com"
|
116
102
|
Factory.build(:user, :name => Proc.new{"jimmy" + " cranky"}).name.should == "jimmy cranky"
|
117
103
|
end
|
104
|
+
|
105
|
+
it "allows factories to call other factories" do
|
106
|
+
Factory.build(:user_manually).address.city.should == "New York"
|
107
|
+
Factory.create(:user_manually).address.city.should == "New York"
|
108
|
+
Factory.create(:user_manually).address.saved?.should == false
|
109
|
+
Factory.build(:user_by_define).address.city.should == "New York"
|
110
|
+
Factory.create(:user_by_define).address.city.should == "New York"
|
111
|
+
Factory.create(:user_by_define).address.saved?.should == true
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should also have its own syntax" do
|
115
|
+
crank(:user).saved?.should == false
|
116
|
+
crank!(:address).saved?.should == true
|
117
|
+
end
|
118
118
|
|
119
119
|
end
|
120
120
|
|
data/spec/spec_helper.rb
CHANGED
@@ -2,13 +2,9 @@ require 'rubygems'
|
|
2
2
|
require 'spec'
|
3
3
|
require 'cranky'
|
4
4
|
|
5
|
-
|
6
|
-
class
|
7
|
-
attr_accessor :
|
8
|
-
attr_accessor :role
|
9
|
-
attr_accessor :email
|
10
|
-
attr_accessor :unique
|
11
|
-
attr_accessor :argument_received
|
5
|
+
|
6
|
+
class TestClass
|
7
|
+
attr_accessor :valid
|
12
8
|
|
13
9
|
def save
|
14
10
|
@saved = true
|
@@ -19,7 +15,7 @@ class User
|
|
19
15
|
end
|
20
16
|
|
21
17
|
def valid?
|
22
|
-
|
18
|
+
@valid
|
23
19
|
end
|
24
20
|
|
25
21
|
def errors
|
@@ -29,11 +25,26 @@ class User
|
|
29
25
|
def attributes
|
30
26
|
self.instance_variables
|
31
27
|
end
|
28
|
+
end
|
32
29
|
|
30
|
+
# A basic model to crank out
|
31
|
+
class User < TestClass
|
32
|
+
attr_accessor :name
|
33
|
+
attr_accessor :role
|
34
|
+
attr_accessor :email
|
35
|
+
attr_accessor :unique
|
36
|
+
attr_accessor :argument_received
|
37
|
+
attr_accessor :address
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
class Address < TestClass
|
42
|
+
attr_accessor :address
|
43
|
+
attr_accessor :city
|
33
44
|
end
|
34
45
|
|
35
46
|
# Some basic factory methods
|
36
|
-
class Cranky
|
47
|
+
class Cranky::Factory
|
37
48
|
|
38
49
|
attr_accessor :some_instance_variable
|
39
50
|
|
@@ -43,6 +54,8 @@ class Cranky
|
|
43
54
|
u.role = options[:role] || :user
|
44
55
|
u.unique = "value#{n}"
|
45
56
|
u.email = "fred@home.com"
|
57
|
+
u.address = Factory.build(:address)
|
58
|
+
u.valid = true
|
46
59
|
u
|
47
60
|
end
|
48
61
|
|
@@ -51,7 +64,9 @@ class Cranky
|
|
51
64
|
:name => "Fred",
|
52
65
|
:role => :user,
|
53
66
|
:unique => "value#{n}",
|
54
|
-
:email => "fred@home.com"
|
67
|
+
:email => "fred@home.com",
|
68
|
+
:address => Factory.create(:address),
|
69
|
+
:valid => true
|
55
70
|
u.argument_received = true if options[:argument_supplied]
|
56
71
|
u
|
57
72
|
end
|
@@ -65,6 +80,12 @@ class Cranky
|
|
65
80
|
inherit(:user_by_define, :role => :admin)
|
66
81
|
end
|
67
82
|
|
83
|
+
def address
|
84
|
+
define :address => "25 Wisteria Lane",
|
85
|
+
:city => "New York",
|
86
|
+
:valid => true
|
87
|
+
end
|
88
|
+
|
68
89
|
end
|
69
90
|
|
70
91
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cranky
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Stephen McGinty
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-08-
|
18
|
+
date: 2010-08-22 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -28,6 +28,9 @@ extensions: []
|
|
28
28
|
extra_rdoc_files: []
|
29
29
|
|
30
30
|
files:
|
31
|
+
- lib/cranky/job.rb
|
32
|
+
- lib/cranky/factory.rb
|
33
|
+
- lib/cranky/version.rb
|
31
34
|
- lib/cranky.rb
|
32
35
|
- spec/spec_helper.rb
|
33
36
|
- spec/cranky_spec.rb
|