saki 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README.markdown +55 -10
  2. data/VERSION +1 -1
  3. data/lib/saki.rb +31 -17
  4. data/saki.gemspec +1 -1
  5. metadata +3 -3
@@ -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 cucumber, but does not sacrifice readability.
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 as "human" as a programming language can get? I hate it too.
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 yadda.yadda }
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 "when a user exists" do
21
+ context "a user exists" do
22
22
  before { @user = Factory :user }
23
- context "when I visit the page for editing that user" do
23
+ context "I visit the page for editing that user" do
24
24
  before { visit edit_user_path(:user) }
25
- it {should yadda.yadda}
25
+ it { should let_me_edit(@user) }
26
26
  end
27
27
  end
28
28
 
29
- I feel Saki is much more expressive.
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 it to on instance variable with the same name.
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
- `on_visiting` takes a path either as a string or as a lambda that executes within a before block to set up the path. This is useful when the code is dependent on an instance variable for path creation.
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
- `where` is a function taking as a parameter either a lambda to execute in the before block, or a symbol which is the name of a function to execute in the before block.
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.3
1
+ 0.0.4
@@ -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
- if path.is_a? String
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
- if path.is_a? String
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
- if executable.is_a? Symbol
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)
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{saki}
8
- s.version = "0.0.3"
8
+ s.version = "0.0.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nate Kidwell"]
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: 25
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
9
+ - 4
10
+ version: 0.0.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nate Kidwell