saki 0.0.3 → 0.0.4
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.
- data/README.markdown +55 -10
- data/VERSION +1 -1
- data/lib/saki.rb +31 -17
- data/saki.gemspec +1 -1
- metadata +3 -3
data/README.markdown
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Saki - For times when you can't swallow Cucumber
|
2
2
|
|
3
|
-
Saki lets you do acceptance testing on top of RSpec. It is considerably more terse than
|
3
|
+
Saki lets you do acceptance testing on top of RSpec. It is considerably more terse than Cucumber, but does not sacrifice readability. Saki also does not use Given/When/Then syntax because the thought is that there is little return other than familiarity for Cucumber users.
|
4
4
|
|
5
|
-
Are you tired of having DRY code, but tests that seem to babble on "for the length of a bible"? Me too. How about RSpec code that is hard to follow, when Ruby itself is
|
5
|
+
Are you tired of having DRY code, but tests that seem to babble on "for the length of a bible"? Me too. How about RSpec code that is hard to follow, when Ruby itself is simple to follow? I hate it too.
|
6
6
|
|
7
7
|
Enter Saki stage left.
|
8
8
|
|
@@ -12,33 +12,70 @@ Well, here's a sample that sets up contexts that create a user and then visit an
|
|
12
12
|
|
13
13
|
with_existing :user do
|
14
14
|
on_visiting edit_path_for(:user) do
|
15
|
-
it { should
|
15
|
+
it { should let_me_edit(@user) }
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
This code basically injects some "before blocks", so it would look like this in vanilla RSpec:
|
20
20
|
|
21
|
-
context "
|
21
|
+
context "a user exists" do
|
22
22
|
before { @user = Factory :user }
|
23
|
-
context "
|
23
|
+
context "I visit the page for editing that user" do
|
24
24
|
before { visit edit_user_path(:user) }
|
25
|
-
|
25
|
+
it { should let_me_edit(@user) }
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
I
|
29
|
+
I believe the Saki example has the following benefits:
|
30
|
+
|
31
|
+
* It saves two lines of code.
|
32
|
+
* It standardizes the code. Whereas a context string might accidentally get out of sync with the code (unmaintained comments, anyone), with Saki this would probably cause a test to fail.
|
33
|
+
* It is much more expressive. You read the test and you immediately know what it does.
|
34
|
+
* Any exceptions to the rule (complicated setups, etc.) now stick out like sore thumbs, as they should.
|
35
|
+
|
36
|
+
The only assumption is that you are using factories instead of fixtures. You also get more out of it in a conventional RESTful application.
|
30
37
|
|
31
38
|
## What class-level methods does it use (for setting up contexts)?
|
32
39
|
|
33
|
-
`with_existing` takes a factory name as a symbol and assigns
|
40
|
+
`with_existing` takes a factory name as a symbol and assigns its created object to on instance variable with the same name.
|
41
|
+
|
42
|
+
`on_visiting` takes a path as a string, or a lambda that executes within a before block to set up the path. It also takes a symbol which is the name of a method name. This is useful when the code is dependent on an instance variable for path creation.
|
43
|
+
|
44
|
+
path_for_user = lambda { user_path(@user) }
|
34
45
|
|
35
|
-
|
46
|
+
on_visiting path_for_user do ...
|
47
|
+
|
48
|
+
or you can do
|
49
|
+
|
50
|
+
def my_user_path
|
51
|
+
user_path(@user)
|
52
|
+
end
|
53
|
+
|
54
|
+
on_visiting :my_user_path do ...
|
36
55
|
|
37
56
|
`on_visiting` has several helper functions for establishing a path: `create_path_for`, `index_path_for`, `edit_path_for`, `show_path_for` and `new_path_for`. These paths all take resource names for establishing a path. In cases where the resource is nested, it has a :parent => parent_resource option. This lets you set up blocks like:
|
38
57
|
|
39
58
|
on_visiting index_path_for(:auction)
|
40
59
|
|
41
|
-
`
|
60
|
+
`on_following_link_to` works the same as on_visiting, but it first validates that the link exists, and then follows it.
|
61
|
+
|
62
|
+
`where` is a function taking as a parameter lambda to execute in the before block.
|
63
|
+
|
64
|
+
def self.creating_a_user
|
65
|
+
lambda {
|
66
|
+
@user = Factory.build @user
|
67
|
+
fill_in "user[email]", :with => @user.email
|
68
|
+
click_button "Create"
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
on_following_link_to create_path_for(:user) do
|
73
|
+
where creating_a_user do
|
74
|
+
specify { page.should have_content(@user.email) }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Obviously the return for this is where you have functions acting as "reusable steps" in the style of Cucumber. In addition your "before blocks" are more expressive.
|
42
79
|
|
43
80
|
Finally, to simplify setting up integration tests, anything you wrap in an `integrate` block (like `describe`) sets the test type to acceptance.
|
44
81
|
|
@@ -58,6 +95,14 @@ Then, as long as your acceptance specs require the acceptance_helper file you sh
|
|
58
95
|
|
59
96
|
It assumes that you are using factory_girl and capybara or webrat, though it probably would work fine with other test factories. If you need another factory in the mix, just redefine the `default_factory` method to behave how you want.
|
60
97
|
|
98
|
+
## References
|
99
|
+
|
100
|
+
The motivation behind my migration from Cucumber and to Saki, are described in blog posts [Encumbered by Cucumber](http://ludicast.com/articles/1), [Introducing Saki](http://ludicast.com/articles/2).
|
101
|
+
|
102
|
+
## Thanks
|
103
|
+
|
104
|
+
The generators are stolen directly from Steak with some minor adjustments.
|
105
|
+
|
61
106
|
## Note on Patches/Pull Requests
|
62
107
|
|
63
108
|
* Fork the project.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
data/lib/saki.rb
CHANGED
@@ -8,6 +8,16 @@ module Saki
|
|
8
8
|
Factory name
|
9
9
|
end
|
10
10
|
|
11
|
+
def get_path(path)
|
12
|
+
if path.is_a? String
|
13
|
+
path
|
14
|
+
elsif path.is_a? Symbol
|
15
|
+
send path
|
16
|
+
else
|
17
|
+
path.call(self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
11
21
|
def add_opts(link, opts)
|
12
22
|
if opts[:parent]
|
13
23
|
link = "/#{opts[:parent].class.to_s.tableize}/#{opts[:parent].id}" + link
|
@@ -56,6 +66,10 @@ module Saki
|
|
56
66
|
end
|
57
67
|
end
|
58
68
|
|
69
|
+
def index_path_for(model)
|
70
|
+
"/#{model}"
|
71
|
+
end
|
72
|
+
|
59
73
|
|
60
74
|
module ClassMethods
|
61
75
|
def with_existing resource, &block
|
@@ -68,11 +82,7 @@ module Saki
|
|
68
82
|
def on_following_link_to path, &block
|
69
83
|
context "on following link" do
|
70
84
|
before do
|
71
|
-
|
72
|
-
path = path
|
73
|
-
else
|
74
|
-
path = path.call(self)
|
75
|
-
end
|
85
|
+
path = get_path(path)
|
76
86
|
has_link(path)
|
77
87
|
visit path
|
78
88
|
end
|
@@ -83,11 +93,7 @@ module Saki
|
|
83
93
|
def on_visiting path, &block
|
84
94
|
context "on visiting" do
|
85
95
|
before do
|
86
|
-
|
87
|
-
visit path
|
88
|
-
else
|
89
|
-
visit path.call(self)
|
90
|
-
end
|
96
|
+
visit get_path(path)
|
91
97
|
end
|
92
98
|
module_eval &block
|
93
99
|
end
|
@@ -106,7 +112,7 @@ module Saki
|
|
106
112
|
add_opts "/#{resource.to_s.pluralize}/#{(context.instance_variable_get('@' + resource.to_s)).id}/edit", opts, context
|
107
113
|
end
|
108
114
|
end
|
109
|
-
|
115
|
+
|
110
116
|
def show_path_for(resource, opts = {})
|
111
117
|
lambda do |context|
|
112
118
|
add_opts "/#{resource.to_s.pluralize}/#{(context.instance_variable_get('@' + resource.to_s)).id}", opts, context
|
@@ -125,13 +131,9 @@ module Saki
|
|
125
131
|
end
|
126
132
|
end
|
127
133
|
|
128
|
-
def where(executable, &block)
|
134
|
+
def where(executable, *opts, &block)
|
129
135
|
context "anonymous closure" do
|
130
|
-
|
131
|
-
before { send executable }
|
132
|
-
else
|
133
|
-
before { instance_eval &executable }
|
134
|
-
end
|
136
|
+
before { instance_eval &executable }
|
135
137
|
module_eval &block
|
136
138
|
end
|
137
139
|
end
|
@@ -139,6 +141,18 @@ module Saki
|
|
139
141
|
end
|
140
142
|
end
|
141
143
|
|
144
|
+
class RSpec::Core::ExampleGroup
|
145
|
+
def method_missing(methId)
|
146
|
+
str = methId.id2name
|
147
|
+
if str.match /(.*)_path/
|
148
|
+
index_path_for($1)
|
149
|
+
else
|
150
|
+
super(methId, [])
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
142
156
|
module RSpec::Core::ObjectExtensions
|
143
157
|
def integrate(*args, &block)
|
144
158
|
args << {} unless args.last.is_a?(Hash)
|
data/saki.gemspec
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Nate Kidwell
|