given_core 3.3.0 → 3.4.0
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.
- checksums.yaml +4 -4
- data/README.md +6 -1
- data/Rakefile +1 -1
- data/doc/article/custom_error_messages.md +282 -0
- data/lib/given/extensions.rb +4 -3
- data/lib/given/failure.rb +2 -1
- data/lib/given/version.rb +1 -1
- metadata +13 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21590386d93b228b7693a69b72ac1a9490153176
|
4
|
+
data.tar.gz: 888675be1bcf66f89099f35a227e1450569e61e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f715fadc107ddab08ae1ed179ab2e5a8baf289fb61ee1a8fda0a7cdb59734e3b8604b08795f8d660e048624e8eb005e87668ac79d0c1f297e93ad588a311f68a
|
7
|
+
data.tar.gz: 3b4552e9a81535deb11e291a349dc71131189e6e6cecdb9e8b3e2407b1deda4023a2c172e8e4a298ad4d6cd404bda7d390ac8a3739a296bcc0ebc3df3eb5d1e0
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
| :----: |
|
5
5
|
| [](https://travis-ci.org/jimweirich/rspec-given) |
|
6
6
|
|
7
|
-
Covering rspec-given, minitest-given, and given-core, version 3.
|
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
@@ -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.
|
data/lib/given/extensions.rb
CHANGED
@@ -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
|
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
data/lib/given/version.rb
CHANGED
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.
|
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-
|
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.
|
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.
|