given_core 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a23d03f3e0141c051c84b2b405c84ab47c15605d
4
- data.tar.gz: d8ddf0ac6cf6289b042f1efbdac1c797c8a2a358
3
+ metadata.gz: 21590386d93b228b7693a69b72ac1a9490153176
4
+ data.tar.gz: 888675be1bcf66f89099f35a227e1450569e61e5
5
5
  SHA512:
6
- metadata.gz: a48e5c92dcef171c12628ddc18e7daac2024e385da10a18f158caca2dbe68f45fa9b75c190732d9cbab7d8bdb2823ae22098e33c58d5e6292929b6c258fe5e8d
7
- data.tar.gz: f9ed5640e35416aa45dc9f64cb60c1bf52a4c6f02334a5f0a28b781d46855f3c2ea810de6011bce1fb2efd53e5abeb2b30403bf5c468f01433442120c64ae36e
6
+ metadata.gz: f715fadc107ddab08ae1ed179ab2e5a8baf289fb61ee1a8fda0a7cdb59734e3b8604b08795f8d660e048624e8eb005e87668ac79d0c1f297e93ad588a311f68a
7
+ data.tar.gz: 3b4552e9a81535deb11e291a349dc71131189e6e6cecdb9e8b3e2407b1deda4023a2c172e8e4a298ad4d6cd404bda7d390ac8a3739a296bcc0ebc3df3eb5d1e0
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  | :----: |
5
5
  | [![Master Build Status](https://secure.travis-ci.org/jimweirich/rspec-given.png?branch=master)](https://travis-ci.org/jimweirich/rspec-given) |
6
6
 
7
- Covering rspec-given, minitest-given, and given-core, version 3.3.0.beta21.
7
+ Covering rspec-given, minitest-given, and given-core, version 3.4.0.
8
8
 
9
9
  rspec-given and minitest-given are extensions to your favorite testing
10
10
  framework to allow Given/When/Then notation when writing specs.
@@ -866,6 +866,11 @@ License. See the MIT-LICENSE file in the source distribution.
866
866
 
867
867
  # History
868
868
 
869
+ * Version 3.4.0
870
+
871
+ * Bare failure objects in Then clauses will now propagate their
872
+ captured failure (added <code>to_bool</code> to failure object).
873
+
869
874
  * Version 3.3.0
870
875
 
871
876
  * Add support for <code>to_bool</code>.
data/Rakefile CHANGED
@@ -42,7 +42,7 @@ task :tag do
42
42
  end
43
43
 
44
44
  desc "Publish the gems"
45
- task :publish => [:clobber, :gem] do
45
+ task :publish_gems => [:clobber, :gem] do
46
46
  FileList['pkg/*.gem'].each do |gemname|
47
47
  sh "gem push #{gemname}"
48
48
  end
@@ -0,0 +1,282 @@
1
+ ## Beautiful Failure Messages
2
+
3
+ The RSpec/Given library is an extension to the RSpec testing framework
4
+ that explicitly supports a Given/When/Then style for testing. It has
5
+ two goals:
6
+
7
+ * Encourage specification language when writing tests
8
+ * Allow beautiful failure messages without writing custom matchers
9
+
10
+ RSpec/Given has a been very successful in both these goals. Consider
11
+ the following spec snippet for a Page object in a Wiki Rails
12
+ application:
13
+
14
+ ```ruby
15
+ describe "content conversion to HTML" do
16
+ Given(:page) {
17
+ Page.new(
18
+ name: "HomePage",
19
+ content: "Have a _nice_ day.")
20
+ }
21
+ Then { page.html_content == "Have a <em>nice</em> day." }
22
+ end
23
+ ```
24
+
25
+ Assuming that the <code>html_content</code> method is incomplete and
26
+ not yet marking emphasized text, the failure message from the
27
+ specification will be:
28
+
29
+ 1) Page content conversion to HTML
30
+ Failure/Error: Then { page.html_content == "Have a <em>nice</em> day." }
31
+ Then expression failed at .../spec/models/page_spec.rb:38
32
+ expected: "Have a _nice_ day."
33
+ to equal: "Have a <em>nice</em> day."
34
+ false <- page.html_content == "Have a <em>nice</em> day."
35
+ "Have a _nice_ day."
36
+ <- page.html_content
37
+ #<Page name: "HomePage", content: "Have a _nice_ day." ...>
38
+ <- page
39
+ # ./spec/models/page_spec.rb:38:in `block in Then'
40
+
41
+ Let's break that down:
42
+
43
+ **It says what failed:**
44
+
45
+ Failure/Error: Then { page.html_content == "Have a <em>nice</em> day." }
46
+
47
+ **It says where it failed:**
48
+
49
+ Then expression failed at .../spec/models/page_spec.rb:38
50
+
51
+ **It says what was expected:**
52
+
53
+ expected: "Have a _nice_ day."
54
+ to equal: "Have a <em>nice</em> day."
55
+
56
+ **It then breaks down each subexpression and displays its value:**
57
+
58
+ false <- page.html_content == "Have a <em>nice</em> day."
59
+ "Have a _nice_ day."
60
+ <- page.html_content
61
+ #<Page name: "HomePage", content: "Have a _nice_ day." ...>
62
+ <- page
63
+
64
+ All of this happens without the developer needing to write any special
65
+ error matchers or custom output. Everything you need to debug a spec
66
+ failure is there in the output.
67
+
68
+ ## A More Complex Example
69
+
70
+ Let's look at a more complex example. Suppose we want to test
71
+ validations in the Page object. For example, we might want to make
72
+ sure that:
73
+
74
+ * The page has a name
75
+ * The name conforms to the standard wiki naming convention (i.e. WikiName).
76
+
77
+ Here's the beginning of that specification:
78
+
79
+ ```ruby
80
+ describe Page do
81
+ VALID_ATTRS = { name: "SomePage", content: "CONTENT" }
82
+ Given(:attrs) { VALID_ATTRS }
83
+ Given(:page) { Page.new(attrs) }
84
+
85
+ ...
86
+ end
87
+ ```
88
+
89
+ <code>VALID\_ATTRS</code> is a list of attributes that will construct a
90
+ valid page object. Normally I would put <code>VALID\_ATTRS</code> in something like
91
+ Factory Girl, but a simple constant is good enough this example.
92
+
93
+ I then declare a given that <code>attrs</code> is the valid
94
+ attributes, and that <code>Page</code> is constructed from these valid
95
+ attributes.
96
+
97
+ I can now describe a valid page object.
98
+
99
+ ```ruby
100
+ context "with valid attributes" do
101
+ Then { page.valid? }
102
+ end
103
+ ```
104
+
105
+ To describe a validation failure where the name is missing, I create a
106
+ context where I override the default <code>attrs</code> with a version
107
+ that omits the name.
108
+
109
+ ```ruby
110
+ context "with missing name" do
111
+ Given(:attrs) { VALID_ATTRS.merge(name: nil) }
112
+ Then { page.invalid? }
113
+ And { ! page.errors[:name].empty? }
114
+ And { page.errors[:name].any? { |msg| msg =~ /blank/ } }
115
+ end
116
+ ```
117
+
118
+ Why Then/And/And? Because there are three things that should be true
119
+ if a validation fails.
120
+
121
+ 1. The object must not be valid
122
+ 2. The field that has the error must have error messages
123
+ 3. At least one of the error messages should mention the word 'blank'
124
+
125
+ Suppose the Page object has a validation on name, but doesn't check
126
+ for presence. The failure message clearly tells you that the spec
127
+ failed because no error messages on the <code>name</code> field
128
+ mentioned 'blank'.
129
+
130
+ 1) Page validations with missing name
131
+ Failure/Error: Then { page.invalid? }
132
+ And expression failed at ./spec/models/page_spec.rb:27
133
+ Failing expression: And { page.errors[:name].any? { |msg| msg =~ /blank/ } }
134
+ false <- page.errors[:name].any? { |msg| msg =~ /blank/ }
135
+ ["is not a wiki name"]
136
+ <- page.errors[:name]
137
+ #<ActiveModel::Errors:... @messages={:name=>["is not a wiki name"]}>
138
+ <- page.errors
139
+ #<Page name: nil, content: "CONTENT", ...>
140
+ <- page
141
+ # ./spec/models/page_spec.rb:25:in `block in Then'
142
+
143
+ We get informative error messages, which is exactly what we want.
144
+
145
+ However, the spec itself is a little wordy, with repeating
146
+ Then/And/And. What if we wrote a simple query function that checked
147
+ for the three conditions and reported true/false accordingly.
148
+
149
+ ```ruby
150
+ def invalid?(page, field, pattern)
151
+ page.invalid? &&
152
+ ! page.errors[field].empty? &&
153
+ page.errors[field].any? { |msg| msg =~ pattern }
154
+ end
155
+ ```
156
+
157
+ Now we can use <code>invalid?</code> in all our validations
158
+ specifications:
159
+
160
+ ```ruby
161
+ context "with missing name" do
162
+ Given(:attrs) { VALID_ATTRS.merge(name: nil) }
163
+ Then { invalid?(page, :name, /blank/) }
164
+ end
165
+ ```
166
+
167
+ But there is a downside. Because <code>invalid?</code> only returns
168
+ true/false, and there are no mention of the <code>errors</code> object
169
+ in the Then clause, the failure message is really uninformative:
170
+
171
+ 1) Page validations with missing name
172
+ Failure/Error: Then { invalid_on(page, :name, /blank/) }
173
+ Then expression failed at ./spec/models/page_spec.rb:31
174
+ false <- invalid_on(page, :name, /blank/)
175
+ #<Page name: nil, content: "CONTENT", ...>
176
+ <- page
177
+ # ./spec/models/page_spec.rb:31:in `block in Then'
178
+
179
+ All we know is that the page is invalid. We get no indication of what
180
+ fields were actually in error and what the error messages actually
181
+ were.
182
+
183
+ ## Custom Failure Message
184
+
185
+ By abstracting away the details how to check for invalid models (which
186
+ is generally a good thing), RSpec/Given lost the ability to give us
187
+ the details of why it failed.
188
+
189
+ Fortunately, there is a simple fix. Instead of returning a simple
190
+ true/false value, the <code>invalid?</code> method should return an
191
+ object, that when inspected, tells why it failed.
192
+
193
+ If a _Then_ clause returns a value that supports a
194
+ <code>to_bool</code> method, then RSpec/Given will call that method
195
+ before checking for true/false (in rspec-given 3.3.0 or later). All we
196
+ need to do is arrange for that object to be returned.
197
+
198
+ ```ruby
199
+ def must_be_invalid(model, field, pattern=//)
200
+ MustBeInvalid.new(model, field, pattern)
201
+ end
202
+ ```
203
+
204
+ Since the method no longer returns a true/false value, I've changed
205
+ the name from <code>invalid?</code> to <code>must\_be\_invalid</code>.
206
+
207
+ The code for the <code>MustBeInvalid</code> class is a bit long, but
208
+ there is nothing complex in it. The <code>to_bool</code> method
209
+ carefully checks for each of our three conditions and records the
210
+ exact reason for failure in the @why instance variable. The
211
+ <code>inspect</code> method (called by RSpec/Given to display its
212
+ value) just returns the @why value with additional details about the
213
+ errors on the object.
214
+
215
+ ```ruby
216
+ class MustBeInvalid
217
+ def initialize(model, field, pattern)
218
+ @model = model
219
+ @field = field
220
+ @pattern = pattern
221
+ @why = nil
222
+ end
223
+
224
+ def to_bool
225
+ if @model.valid?
226
+ @why = "#{@model.class} was valid (expected invalid)"
227
+ false
228
+ elsif @model.errors[@field].empty?
229
+ @why = "#{@model.class} had no errors on field #{@field}" +
230
+ error_descriptions
231
+ false
232
+ elsif @model.errors[@field].none? { |msg| msg =~ @pattern }
233
+ @why = "#{@model.class} had no errors " +
234
+ "matching #{@pattern} on field #{@field}" +
235
+ error_descriptions
236
+ false
237
+ else
238
+ @why = "OK (expected invalid)"
239
+ true
240
+ end
241
+ end
242
+
243
+ def inspect
244
+ to_bool if @why.nil?
245
+ @why
246
+ end
247
+
248
+ private
249
+
250
+ def error_descriptions
251
+ if @model.errors.empty?
252
+ ""
253
+ else
254
+ "\n Errors were:\n * " +
255
+ @model.errors.full_messages.
256
+ map { |msg| msg }.join("\n * ")
257
+ end
258
+ end
259
+ end
260
+ ```
261
+
262
+ The failure message returned by <code>MustBeInvalid</code> is once
263
+ again clear and to the point. It contains all the information needed
264
+ for debugging.
265
+
266
+ 1) Page validations with missing name
267
+ Failure/Error: Then { must_be_invalid(page, :name, /blank/) }
268
+ Then expression failed at ./spec/models/page_spec.rb:31
269
+ Page had no errors matching (?-mix:blank) on field name
270
+ Errors were:
271
+ * Name is not a wiki name
272
+ <- must_be_invalid(page, :name, /blank/)
273
+ #<Page name: nil, content: "CONTENT", ...>
274
+ <- page
275
+ # ./spec/models/page_spec.rb:31:in `block in Then'
276
+
277
+ ## Summary
278
+
279
+ I've always felt that you can tell the maturity level of a piece of
280
+ software by the beauty of the error messages it produces. By
281
+ providing the ability to do custom messages where needed, RSpec/Given
282
+ takes a step in that direction.
@@ -104,7 +104,7 @@ module Given
104
104
  end
105
105
 
106
106
  # Determine of the natural assertion pass/fail status of the block
107
- def _gvn_block_passed?(block)
107
+ def _gvn_block_passed?(block) # :nodoc:
108
108
  passed = instance_eval(&block)
109
109
  passed = passed.to_bool if passed.respond_to?(:to_bool)
110
110
  passed
@@ -247,11 +247,12 @@ module Given
247
247
  # :call-seq:
248
248
  # Then { ... assertion ... }
249
249
  #
250
- def Then(&block)
250
+ def Then(opts={}, &block)
251
+ on_eval = opts.fetch(:on_eval, "_gvn_then")
251
252
  file, line = Given.location_of(block)
252
253
  description = _Gvn_lines.line(file, line) unless Given.source_caching_disabled
253
254
  cmd = description ? "it(description)" : "specify"
254
- eval %{#{cmd} do _gvn_then(&block) end}, binding, file, line
255
+ eval %{#{cmd} do #{on_eval}(&block) end}, binding, file, line
255
256
  _Gvn_context_info[:then_defined] = true
256
257
  end
257
258
 
data/lib/given/failure.rb CHANGED
@@ -60,7 +60,8 @@ module Given
60
60
  method_symbol == :call ||
61
61
  method_symbol == :== ||
62
62
  method_symbol == :!= ||
63
- method_symbol == :is_a?
63
+ method_symbol == :is_a? ||
64
+ method_symbol == :to_bool
64
65
  end
65
66
 
66
67
  private
data/lib/given/version.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  module Given
3
3
  VERSION_NUMBERS = [
4
4
  VERSION_MAJOR = 3,
5
- VERSION_MINOR = 3,
5
+ VERSION_MINOR = 4,
6
6
  VERSION_BUILD = 0,
7
7
  ]
8
8
  VERSION = VERSION_NUMBERS.join(".")
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: given_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Weirich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-20 00:00:00.000000000 Z
11
+ date: 2013-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sorcerer
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 0.3.7
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.3.7
27
27
  description: |
@@ -39,6 +39,8 @@ files:
39
39
  - README.md
40
40
  - Rakefile
41
41
  - TODO
42
+ - doc/article/custom_error_messages.md
43
+ - doc/main.rdoc
42
44
  - lib/given.rb
43
45
  - lib/given/assertions.rb
44
46
  - lib/given/binary_operation.rb
@@ -75,34 +77,33 @@ files:
75
77
  - rakelib/gemspec.rake
76
78
  - rakelib/metrics.rake
77
79
  - rakelib/preview.rake
78
- - doc/main.rdoc
79
80
  homepage: http://github.com/jimweirich/rspec-given
80
81
  licenses:
81
82
  - MIT
82
83
  metadata: {}
83
84
  post_install_message:
84
85
  rdoc_options:
85
- - --line-numbers
86
- - --inline-source
87
- - --main
86
+ - "--line-numbers"
87
+ - "--inline-source"
88
+ - "--main"
88
89
  - doc/main.rdoc
89
- - --title
90
+ - "--title"
90
91
  - RSpec Given Extensions
91
92
  require_paths:
92
93
  - lib
93
94
  required_ruby_version: !ruby/object:Gem::Requirement
94
95
  requirements:
95
- - - '>='
96
+ - - ">="
96
97
  - !ruby/object:Gem::Version
97
98
  version: 1.9.2
98
99
  required_rubygems_version: !ruby/object:Gem::Requirement
99
100
  requirements:
100
- - - '>='
101
+ - - ">="
101
102
  - !ruby/object:Gem::Version
102
103
  version: '0'
103
104
  requirements: []
104
105
  rubyforge_project: given
105
- rubygems_version: 2.1.11
106
+ rubygems_version: 2.2.0
106
107
  signing_key:
107
108
  specification_version: 4
108
109
  summary: Core engine for RSpec::Given and Minitest::Given.