rspec-given 2.2.1 → 2.3.0.beta.1
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/Gemfile +7 -0
- data/Gemfile.lock +19 -0
- data/README.md +199 -21
- data/Rakefile +12 -12
- data/examples/failing/natural_failing_spec.rb +36 -0
- data/examples/failing/sample_spec.rb +7 -0
- data/examples/integration/then_spec.rb +9 -0
- data/lib/rspec/given.rb +2 -7
- data/lib/rspec/given/configure.rb +2 -0
- data/lib/rspec/given/core.rb +9 -0
- data/lib/rspec/given/extensions.rb +57 -18
- data/lib/rspec/given/module_methods.rb +8 -0
- data/lib/rspec/given/natural_assertion.rb +178 -0
- data/lib/rspec/given/version.rb +4 -2
- metadata +43 -9
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
+
bluecloth (2.2.0)
|
4
5
|
diff-lcs (1.1.3)
|
6
|
+
flog (3.2.1)
|
7
|
+
ruby_parser (~> 3.1, > 3.1.0)
|
8
|
+
sexp_processor (~> 4.0)
|
9
|
+
ghpreview (0.0.2)
|
10
|
+
httpclient
|
11
|
+
listen
|
12
|
+
rb-fsevent
|
13
|
+
httpclient (2.3.0.1)
|
14
|
+
listen (0.6.0)
|
5
15
|
rake (0.9.2.2)
|
16
|
+
rb-fsevent (0.9.2)
|
6
17
|
rspec (2.12.0)
|
7
18
|
rspec-core (~> 2.12.0)
|
8
19
|
rspec-expectations (~> 2.12.0)
|
@@ -11,10 +22,18 @@ GEM
|
|
11
22
|
rspec-expectations (2.12.0)
|
12
23
|
diff-lcs (~> 1.1.3)
|
13
24
|
rspec-mocks (2.12.0)
|
25
|
+
ruby_parser (3.1.1)
|
26
|
+
sexp_processor (~> 4.1)
|
27
|
+
sexp_processor (4.1.3)
|
28
|
+
sorcerer (0.3.7)
|
14
29
|
|
15
30
|
PLATFORMS
|
16
31
|
ruby
|
17
32
|
|
18
33
|
DEPENDENCIES
|
34
|
+
bluecloth
|
35
|
+
flog
|
36
|
+
ghpreview
|
19
37
|
rake (>= 0.9.2.2)
|
20
38
|
rspec (>= 2.12)
|
39
|
+
sorcerer
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# rspec-given
|
2
2
|
|
3
|
-
Covering rspec-given, version
|
3
|
+
Covering rspec-given, version 3.0.0.beta.1.
|
4
4
|
|
5
5
|
rspec-given is an RSpec extension to allow Given/When/Then notation in
|
6
6
|
RSpec specifications. It is a natural extension of the experimental
|
@@ -232,7 +232,7 @@ Let me repeat that: <b>_Then_ clauses should not have any side
|
|
232
232
|
effects!</b> _Then_ clauses with side effects are erroneous. _Then_
|
233
233
|
clauses need to be idempotent, so that running them once, twice, a
|
234
234
|
hundred times, or never does not change the state of the program. (The
|
235
|
-
same is true of _And_ clauses).
|
235
|
+
same is true of _And_ and _Invariant_ clauses).
|
236
236
|
|
237
237
|
In RSpec terms, a _Then_ clause forms a RSpec Example that runs in the
|
238
238
|
context of an Example Group (defined by a describe or context clause).
|
@@ -261,7 +261,7 @@ The _And_ clause is similar to _Then_, but does not form its own RSpec
|
|
261
261
|
example. This means that _And_ clauses reuse the setup from a sibling
|
262
262
|
_Then_ clause. Using a single _Then_ an multiple _And_ clauses in an
|
263
263
|
example group means the setup for that group is run only once (for the
|
264
|
-
_Then_ clause) and reused for all the
|
264
|
+
_Then_ clause) and reused for all the _And_ clauses. This can be a
|
265
265
|
significant speed savings where the setup for an example group is
|
266
266
|
expensive.
|
267
267
|
|
@@ -325,6 +325,194 @@ Notes:
|
|
325
325
|
represented in the RSpec formatted output (e.g. the '--format html'
|
326
326
|
option).
|
327
327
|
|
328
|
+
## Natural Assertions
|
329
|
+
|
330
|
+
**NOTE:** <em>Natural assertions are an experimental feature of
|
331
|
+
RSpec/Given. They are currently disabled by default.</em>
|
332
|
+
|
333
|
+
RSpec/Given now supports the use of "natural assertions" in _Then_,
|
334
|
+
_And_, and _Invariant_ blocks. Natural assertions are just Ruby
|
335
|
+
conditionals, without the _should_ or _expect_ methods that RSpec
|
336
|
+
provides. Here are the Then/And examples from above, but written using
|
337
|
+
natural assertions:
|
338
|
+
|
339
|
+
```ruby
|
340
|
+
Then { pop_result == :top_item }
|
341
|
+
And { stack.top == :second_item }
|
342
|
+
And { stack.depth == original_depth - 1 }
|
343
|
+
```
|
344
|
+
|
345
|
+
Natural assertions must be enabled, either globally or on a per
|
346
|
+
context basis, to be recognized.
|
347
|
+
|
348
|
+
### Failure Messages with Natural Assertions
|
349
|
+
|
350
|
+
Since natural assertions do not depend upon matchers, you don't get
|
351
|
+
customized error messages from them. What you _do_ get is a complete
|
352
|
+
analsysis of the expression that failed.
|
353
|
+
|
354
|
+
For example, given the following failing specification:
|
355
|
+
|
356
|
+
```ruby
|
357
|
+
RSpec::Given.use_natural_assertions
|
358
|
+
|
359
|
+
describe "Natural Assertions" do
|
360
|
+
Given(:foo) { 1 }
|
361
|
+
Given(:bar) { 2 }
|
362
|
+
Then { foo + bar == 2 }
|
363
|
+
end
|
364
|
+
```
|
365
|
+
|
366
|
+
You would get:
|
367
|
+
|
368
|
+
```
|
369
|
+
1) Natural Assertions
|
370
|
+
Failure/Error: Then { foo + bar == 2 }
|
371
|
+
Then expression failed at /Users/jim/working/git/rspec-given/examples/failing/sample_spec.rb:6
|
372
|
+
expected: 3
|
373
|
+
to equal: 2
|
374
|
+
false <- foo + bar == 2
|
375
|
+
3 <- foo + bar
|
376
|
+
1 <- foo
|
377
|
+
2 <- bar
|
378
|
+
# ./examples/failing/sample_spec.rb:6:in `block in Then'
|
379
|
+
```
|
380
|
+
|
381
|
+
Notice how the failing expression "<code>foo+bar == 2</code>" was
|
382
|
+
broken down into subexpressions and values for each subexpression.
|
383
|
+
This gives you all the information you need to figure out exactly what
|
384
|
+
part of the expression is causing the failure.
|
385
|
+
|
386
|
+
Natural expressions will give additional information (e.g. "expected:
|
387
|
+
3 to equal: 2") for top level expressions involving any of the
|
388
|
+
comparison operators (==, !=, <, <=, >, >=) or matching operators (=~,
|
389
|
+
!~).
|
390
|
+
|
391
|
+
### Caveats on Natural Assertions
|
392
|
+
|
393
|
+
Keep the following in mind when using Natural Assertions.
|
394
|
+
|
395
|
+
* Only a single assertion per _Then_. Only the final expression of the
|
396
|
+
_Then_ block will be considered when determining pass/fail for the
|
397
|
+
assertion. If you _want_ to express a complex condition for the
|
398
|
+
_Then_, you need to use ||, && or some other logical operation to
|
399
|
+
join the conditions into a single expression (and the failure
|
400
|
+
message will breakdown the values for each part).
|
401
|
+
|
402
|
+
* Natural assertions must be **idempotent** (that means they don't
|
403
|
+
change anything). Since the natural assertion error message contains
|
404
|
+
the values of all the subexpressions, the expression and its
|
405
|
+
subexpressions will be evaluated multiple times. If the block is not
|
406
|
+
idempotent, you will get changing answers as the subexpressions are
|
407
|
+
evaluated.
|
408
|
+
|
409
|
+
That last point is important. If you write code like this:
|
410
|
+
|
411
|
+
```ruby
|
412
|
+
# DO NOT WRITE CODE LIKE THIS
|
413
|
+
context "Incorrect non-idempotent conditions" do
|
414
|
+
Given(:ary) { [1, 2, 3] }
|
415
|
+
Then { ary.delete(1) == nil }
|
416
|
+
end
|
417
|
+
```
|
418
|
+
|
419
|
+
Then the assertion will fail (because <code>ary.delete(1)</code> will
|
420
|
+
initially return 1). But when the error message is formated, the
|
421
|
+
system reports that <code>ary.delete(1)</code> returns nil. You will
|
422
|
+
scratch your head over that for a good while.
|
423
|
+
|
424
|
+
Instead, move the state changing code into a When block, then just
|
425
|
+
assert what you need about the result from the when block. Something
|
426
|
+
like this is good:
|
427
|
+
|
428
|
+
```ruby
|
429
|
+
context "Correct idempotent conditions" do
|
430
|
+
Given(:ary) { [1, 2, 3] }
|
431
|
+
When(:result) { ary.delete(1) }
|
432
|
+
Then { result == nil }
|
433
|
+
end
|
434
|
+
```
|
435
|
+
|
436
|
+
### Mixing Natural Assertions and RSpec Assertions
|
437
|
+
|
438
|
+
Natural assertions and RSpec assertions for the most part can be
|
439
|
+
intermixed in a single test suite, even within a single context.
|
440
|
+
Because there are a few corner cases that might cause problems, they
|
441
|
+
must be explicitly enabled before they will be considered.
|
442
|
+
|
443
|
+
To enable natural assertions in a context, call the
|
444
|
+
_use_natural_assertions_ method in that context. For example:
|
445
|
+
|
446
|
+
```ruby
|
447
|
+
context "Outer" do
|
448
|
+
use_natural_assertions
|
449
|
+
|
450
|
+
context "Inner" do
|
451
|
+
end
|
452
|
+
|
453
|
+
context "Disabled" do
|
454
|
+
use_natural_assertions false
|
455
|
+
end
|
456
|
+
end
|
457
|
+
```
|
458
|
+
|
459
|
+
Both the _Outer_ and _Inner_ contexts will use natural assertions. The
|
460
|
+
_Disabled_ context overrides the setting inherited from _Outer_ and
|
461
|
+
will not process natural assertions.
|
462
|
+
|
463
|
+
See the **configuration** section below to see how to enable natural
|
464
|
+
assertions project wide.
|
465
|
+
|
466
|
+
### Processing Natural Assertions
|
467
|
+
|
468
|
+
When natural assertions are enabled, they are only used if:
|
469
|
+
|
470
|
+
1. The block does not throw an RSpec assertion failure (or any other
|
471
|
+
exception for that matter).
|
472
|
+
|
473
|
+
1. The block returns false (blocks that return true pass the
|
474
|
+
assertion and don't need a failure message).
|
475
|
+
|
476
|
+
1. The block does not directly use RSpec's _should_ or _expect_
|
477
|
+
method.
|
478
|
+
|
479
|
+
Detecting that last point (the use of _should_ and _expect_) is done
|
480
|
+
by examining the source of the block. However it is not always
|
481
|
+
possible to correctly determine if _should_ and _expect_ are used
|
482
|
+
merely from the source. Fortunately you can always override the
|
483
|
+
automatic detection by either forcing natural assertions (with
|
484
|
+
:always) or disabling them (with false).
|
485
|
+
|
486
|
+
```ruby
|
487
|
+
# Then uses a non-RSpec version of "should" on
|
488
|
+
context "sample" do
|
489
|
+
use_natural_assertions :always
|
490
|
+
Then { obj.should } # Non-rspec should is called
|
491
|
+
end
|
492
|
+
```
|
493
|
+
|
494
|
+
```ruby
|
495
|
+
# Then does not call should directly, so disable natural assertions
|
496
|
+
context "sample" do
|
497
|
+
use_natural_assertions false
|
498
|
+
Then { check_validations(obj) } # check_validations calls "should" internally
|
499
|
+
end
|
500
|
+
|
501
|
+
# You can also just not use a Then for that condition.
|
502
|
+
context "another sample" do
|
503
|
+
it "checks validations" do
|
504
|
+
check_validations(obj)
|
505
|
+
end
|
506
|
+
end
|
507
|
+
```
|
508
|
+
|
509
|
+
### Further Reading
|
510
|
+
|
511
|
+
Natural assertions were inspired by the [wrong assertion
|
512
|
+
library](http://rubygems.org/gems/wrong) by [Alex
|
513
|
+
Chaffee](http://rubygems.org/profiles/alexch) and [Steve
|
514
|
+
Conover](http://rubygems.org/profiles/sconoversf).
|
515
|
+
|
328
516
|
## Configuration
|
329
517
|
|
330
518
|
Just require 'rspec/given' in the spec helper of your project and it
|
@@ -340,28 +528,18 @@ unconditionally, then add the following line to your spec helper file:
|
|
340
528
|
RSpec::Given.source_caching_disabled = true
|
341
529
|
```
|
342
530
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
tests are much more like specifications when I use it. However, I'm
|
347
|
-
not entirely happy with it.
|
348
|
-
|
349
|
-
I would like to remove the need for the ".should" in all the _Then_
|
350
|
-
clauses. In other words, instead of saying:
|
531
|
+
Natural assertions are disabled by default. To globally configure
|
532
|
+
natural assertions, add one of the following lines to your spec_helper
|
533
|
+
file:
|
351
534
|
|
352
535
|
```ruby
|
353
|
-
|
536
|
+
RSpec::Given.use_natural_assertions # Enable natural assertions
|
537
|
+
RSpec::Given.use_natural_assertions true # Same as above
|
538
|
+
RSpec::Given.use_natural_assertions false # Disable natural assertions
|
539
|
+
RSpec::Given.use_natural_assertions :always # Always process natural assertions
|
540
|
+
# ... even when should/expect are detected
|
354
541
|
```
|
355
542
|
|
356
|
-
we could say:
|
357
|
-
|
358
|
-
```ruby
|
359
|
-
Then { x == y }
|
360
|
-
```
|
361
|
-
|
362
|
-
I think the [wrong assertion library](http://rubygems.org/gems/wrong)
|
363
|
-
has laid some groundwork in this area.
|
364
|
-
|
365
543
|
# Links
|
366
544
|
|
367
545
|
* Github: [https://github.com/jimweirich/rspec-given](https://github.com/jimweirich/rspec-given)
|
data/Rakefile
CHANGED
@@ -66,10 +66,19 @@ task :examples1 => [:verify_rspec1] do
|
|
66
66
|
sh "spec examples/stack/stack_spec1.rb"
|
67
67
|
end
|
68
68
|
|
69
|
+
EXAMPLES = FileList['examples/**/*_spec.rb'].exclude('examples/failing/*.rb')
|
70
|
+
FAILING_EXAMPLES = FileList['examples/failing/**/*_spec.rb']
|
71
|
+
|
69
72
|
desc "Run the examples in RSpec 2"
|
70
73
|
task :examples2 => [:verify_rspec2] do
|
71
74
|
puts "Running examples (with RSpec2)"
|
72
|
-
sh "rspec
|
75
|
+
sh "rspec #{EXAMPLES}"
|
76
|
+
end
|
77
|
+
|
78
|
+
desc "Run failing examples"
|
79
|
+
task :failing => [:verify_rspec2] do
|
80
|
+
puts "Running failing examples (with RSpec2)"
|
81
|
+
sh "rspec #{FAILING_EXAMPLES}"
|
73
82
|
end
|
74
83
|
|
75
84
|
task :verify_rspec1 do
|
@@ -89,17 +98,8 @@ end
|
|
89
98
|
directory 'html'
|
90
99
|
|
91
100
|
desc "Display the README file"
|
92
|
-
task :readme => "
|
93
|
-
sh "
|
94
|
-
end
|
95
|
-
|
96
|
-
desc "format the README file"
|
97
|
-
task "html/README.html" => ['html', 'README.md'] do
|
98
|
-
open("README.md") do |source|
|
99
|
-
open('html/README.html', 'w') do |out|
|
100
|
-
out.write(BlueCloth.new(source.read).to_html)
|
101
|
-
end
|
102
|
-
end
|
101
|
+
task :readme => ["README.md"] do
|
102
|
+
sh "ghpreview README.md"
|
103
103
|
end
|
104
104
|
|
105
105
|
desc "Generate an RDoc README"
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rspec/given'
|
2
|
+
require 'rspec/given/natural_assertion'
|
3
|
+
|
4
|
+
describe "Natural Assertions" do
|
5
|
+
use_natural_assertions
|
6
|
+
|
7
|
+
Given(:foo) { 1 }
|
8
|
+
Given(:expected) { 2 }
|
9
|
+
Given(:ary) { [1] }
|
10
|
+
Given(:empty) { [] }
|
11
|
+
Given(:null) { nil }
|
12
|
+
Then { foo+foo+2*foo == expected }
|
13
|
+
Then { nil == "HI" && true && :symbol && 1}
|
14
|
+
Then { foo.should == 2 }
|
15
|
+
Then { foo != 1 }
|
16
|
+
Then { foo.should_not == 1 }
|
17
|
+
Then { foo.should be_nil }
|
18
|
+
Then { ary.empty? }
|
19
|
+
Then { !null.nil? }
|
20
|
+
Then { fail "OUCH" }
|
21
|
+
Then { ! empty.empty? }
|
22
|
+
Then {
|
23
|
+
(puts "Ha ha world", ! true)
|
24
|
+
}
|
25
|
+
|
26
|
+
context "Incorrect non-idempotent conditions" do
|
27
|
+
Given(:ary) { [1, 2, 3] }
|
28
|
+
Then { ary.delete(1) == nil }
|
29
|
+
end
|
30
|
+
|
31
|
+
context "Correct idempotent conditions" do
|
32
|
+
Given(:ary) { [1, 2, 3] }
|
33
|
+
When(:result) { ary.delete(1) }
|
34
|
+
Then { result == nil }
|
35
|
+
end
|
36
|
+
end
|
data/lib/rspec/given.rb
CHANGED
@@ -12,12 +12,7 @@ end
|
|
12
12
|
if RSpec::Given.using_old_rspec?
|
13
13
|
require 'rspec/given/rspec1_given'
|
14
14
|
else
|
15
|
-
require 'rspec/given/
|
16
|
-
require 'rspec/given/module_methods'
|
17
|
-
require 'rspec/given/file_cache'
|
18
|
-
require 'rspec/given/line_extractor'
|
19
|
-
require 'rspec/given/extensions'
|
20
|
-
require 'rspec/given/configure'
|
21
|
-
require 'rspec/given/failure'
|
15
|
+
require 'rspec/given/core'
|
22
16
|
require 'rspec/given/have_failed'
|
17
|
+
require 'rspec/given/configure'
|
23
18
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rspec/given/failure'
|
2
2
|
require 'rspec/given/module_methods'
|
3
|
+
require 'rspec/given/natural_assertion'
|
3
4
|
|
4
5
|
module RSpec
|
5
6
|
module Given
|
@@ -9,25 +10,47 @@ module RSpec
|
|
9
10
|
# implementation-specific.
|
10
11
|
module InstanceExtensions # :nodoc:
|
11
12
|
|
13
|
+
# List of containing contexts in order from innermost to
|
14
|
+
# outermost.
|
15
|
+
def _rg_inner_contexts # :nodoc:
|
16
|
+
self.class.ancestors.select { |context|
|
17
|
+
context.respond_to?(:_rg_givens)
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
12
21
|
# List of containing contexts in order from outermost to
|
13
22
|
# innermost.
|
14
23
|
def _rg_contexts # :nodoc:
|
15
|
-
|
16
|
-
|
17
|
-
|
24
|
+
_rg_inner_contexts.reverse
|
25
|
+
end
|
26
|
+
|
27
|
+
def _rg_info(keyword)
|
28
|
+
_rg_inner_contexts.each do |context|
|
29
|
+
h = context._rg_context_info
|
30
|
+
if h.has_key?(keyword)
|
31
|
+
return h[keyword]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def _rg_natural_assertions?(nassert)
|
38
|
+
info_value = _rg_info(:natural_assertions_enabled)
|
39
|
+
use_na = info_value.nil? ? RSpec::Given.natural_assertions_enabled? : info_value
|
40
|
+
(! nassert.using_rspec_assertion? && use_na) || (use_na == :always)
|
18
41
|
end
|
19
42
|
|
20
43
|
# Establish all the Given preconditions the current and
|
21
44
|
# surrounding describe/context blocks, starting with the
|
22
45
|
# outermost context.
|
23
46
|
def _rg_establish_givens # :nodoc:
|
24
|
-
return if defined?(@_rg_ran)
|
47
|
+
return if defined?(@_rg_ran) && @_rg_ran
|
48
|
+
@_rg_ran = true
|
25
49
|
_rg_contexts.each do |context|
|
26
50
|
context._rg_givens.each do |block|
|
27
51
|
instance_eval(&block)
|
28
52
|
end
|
29
53
|
end
|
30
|
-
@_rg_ran = true
|
31
54
|
end
|
32
55
|
|
33
56
|
# Check all the invariants in the current and surrounding
|
@@ -35,7 +58,7 @@ module RSpec
|
|
35
58
|
def _rg_check_invariants # :nodoc:
|
36
59
|
_rg_contexts.each do |context|
|
37
60
|
context._rg_invariants.each do |block|
|
38
|
-
|
61
|
+
_rg_evaluate(block)
|
39
62
|
end
|
40
63
|
end
|
41
64
|
end
|
@@ -43,7 +66,7 @@ module RSpec
|
|
43
66
|
def _rg_check_ands # :nodoc:
|
44
67
|
return if self.class._rg_context_info[:and_ran]
|
45
68
|
self.class._rg_and_blocks.each do |block|
|
46
|
-
|
69
|
+
_rg_evaluate(block)
|
47
70
|
end
|
48
71
|
self.class._rg_context_info[:and_ran] = true
|
49
72
|
end
|
@@ -52,9 +75,18 @@ module RSpec
|
|
52
75
|
def _rg_then(&block) # :nodoc:
|
53
76
|
_rg_establish_givens
|
54
77
|
_rg_check_invariants
|
55
|
-
|
78
|
+
_rg_evaluate(block)
|
56
79
|
_rg_check_ands
|
57
80
|
end
|
81
|
+
|
82
|
+
def _rg_evaluate(block)
|
83
|
+
unless instance_eval(&block)
|
84
|
+
nassert = NaturalAssertion.new(block, binding, self.class._rg_lines)
|
85
|
+
if ! nassert.using_rspec_assertion? && nassert.has_content?
|
86
|
+
::RSpec::Expectations.fail_with nassert.message
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
58
90
|
end
|
59
91
|
|
60
92
|
module ClassExtensions
|
@@ -76,7 +108,7 @@ module RSpec
|
|
76
108
|
end
|
77
109
|
|
78
110
|
def _rg_context_info
|
79
|
-
@
|
111
|
+
@_rg_context_info ||= {}
|
80
112
|
end
|
81
113
|
|
82
114
|
def _rg_lines
|
@@ -106,8 +138,7 @@ module RSpec
|
|
106
138
|
# Scenario "a scenario description" do ... end
|
107
139
|
#
|
108
140
|
def Scenario(description, &block)
|
109
|
-
line = eval("__LINE__", block.binding)
|
110
|
-
file = eval("__FILE__", block.binding)
|
141
|
+
file, line = eval("[__LINE__, __FILE__]", block.binding)
|
111
142
|
puts "WARNING: Scenario is deprecated, please use either describe or context (#{file}:#{line})"
|
112
143
|
context(description, &block)
|
113
144
|
end
|
@@ -119,8 +150,8 @@ module RSpec
|
|
119
150
|
# every time the specification is executed.
|
120
151
|
#
|
121
152
|
# :call-seq:
|
122
|
-
# Given(:name
|
123
|
-
# Given
|
153
|
+
# Given(:name) { ... code ... }
|
154
|
+
# Given { ... code ... }
|
124
155
|
#
|
125
156
|
def Given(*args, &block)
|
126
157
|
if args.first.is_a?(Symbol)
|
@@ -136,6 +167,7 @@ module RSpec
|
|
136
167
|
#
|
137
168
|
# :call-seq:
|
138
169
|
# Given!(:name) { ... code ... }
|
170
|
+
#
|
139
171
|
def Given!(name, &block)
|
140
172
|
let!(name, &block)
|
141
173
|
_rg_givens << _rg_trigger_given(name)
|
@@ -144,8 +176,8 @@ module RSpec
|
|
144
176
|
# Declare the code that is under test.
|
145
177
|
#
|
146
178
|
# :call-seq:
|
147
|
-
# When(:named_result
|
148
|
-
# When
|
179
|
+
# When(:named_result) { ... code_under_test ... }
|
180
|
+
# When { ... code_under_test ... }
|
149
181
|
#
|
150
182
|
def When(*args, &block)
|
151
183
|
if args.first.is_a?(Symbol)
|
@@ -170,10 +202,13 @@ module RSpec
|
|
170
202
|
# Then supplies an assertion that should be true after all the
|
171
203
|
# Given and When blocks have been run. All invariants in scope
|
172
204
|
# will be checked before the Then block is run.
|
205
|
+
#
|
206
|
+
# :call-seq:
|
207
|
+
# Then { ... assertion ... }
|
208
|
+
#
|
173
209
|
def Then(&block)
|
174
|
-
|
175
|
-
file = eval "__FILE__",
|
176
|
-
line = eval "__LINE__", b
|
210
|
+
env = block.binding
|
211
|
+
file, line = eval "[__FILE__, __LINE__]", env
|
177
212
|
description = _rg_lines.line(file, line) unless RSpec::Given.source_caching_disabled
|
178
213
|
if description
|
179
214
|
cmd = "it(description)"
|
@@ -194,6 +229,10 @@ module RSpec
|
|
194
229
|
fail "And defined without a Then" unless _rg_context_info[:then_defined]
|
195
230
|
_rg_and_blocks << block
|
196
231
|
end
|
232
|
+
|
233
|
+
def use_natural_assertions(enabled=true)
|
234
|
+
_rg_context_info[:natural_assertions_enabled] = enabled
|
235
|
+
end
|
197
236
|
end
|
198
237
|
end
|
199
238
|
end
|
@@ -12,5 +12,13 @@ module RSpec
|
|
12
12
|
format_active = c.formatters.any? { |f| f.class.name !~ /ProgressFormatter/ }
|
13
13
|
RSpec::Given.source_caching_disabled = ! format_active
|
14
14
|
end
|
15
|
+
|
16
|
+
def self.use_natural_assertions(enabled=true)
|
17
|
+
@natural_assertions_enabled = enabled
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.natural_assertions_enabled?
|
21
|
+
@natural_assertions_enabled
|
22
|
+
end
|
15
23
|
end
|
16
24
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'ripper'
|
2
|
+
require 'sorcerer'
|
3
|
+
|
4
|
+
module RSpec
|
5
|
+
module Given
|
6
|
+
|
7
|
+
InvalidThenError = Class.new(StandardError)
|
8
|
+
|
9
|
+
class EvalErr
|
10
|
+
def initialize(str)
|
11
|
+
@string = str
|
12
|
+
end
|
13
|
+
def size
|
14
|
+
inspect.size
|
15
|
+
end
|
16
|
+
def to_s
|
17
|
+
@string
|
18
|
+
end
|
19
|
+
def inspect
|
20
|
+
@string
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class NaturalAssertion
|
25
|
+
|
26
|
+
def initialize(block, env, line_extractor)
|
27
|
+
@block = block
|
28
|
+
@env = env
|
29
|
+
@line_extractor = line_extractor
|
30
|
+
set_file_and_line
|
31
|
+
end
|
32
|
+
|
33
|
+
VOID_SEXP = [:void_stmt]
|
34
|
+
|
35
|
+
def using_rspec_assertion?
|
36
|
+
using_should? || using_expect?
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_content?
|
40
|
+
assertion_sexp != VOID_SEXP
|
41
|
+
end
|
42
|
+
|
43
|
+
def message
|
44
|
+
@output = "Then expression failed at #{source_line}\n"
|
45
|
+
explain_failure
|
46
|
+
display_pairs(expression_value_pairs)
|
47
|
+
@output << "\n"
|
48
|
+
@output
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def using_should?
|
54
|
+
source =~ /\.\s*should(_not)?\b/
|
55
|
+
end
|
56
|
+
|
57
|
+
def using_expect?
|
58
|
+
source =~ /\bexpect\s*[({].*[)}]\s*\.\s*(not_)?to\b/
|
59
|
+
end
|
60
|
+
|
61
|
+
BINARY_EXPLAINATIONS = {
|
62
|
+
:== => "to equal",
|
63
|
+
:!= => "to not equal",
|
64
|
+
:< => "to be less than",
|
65
|
+
:<= => "to be less or equal to",
|
66
|
+
:> => "to be greater than",
|
67
|
+
:>= => "to be greater or equal to",
|
68
|
+
:=~ => "to match",
|
69
|
+
:!~ => "to not match",
|
70
|
+
}
|
71
|
+
|
72
|
+
def explain_failure
|
73
|
+
if assertion_sexp.first == :binary && msg = BINARY_EXPLAINATIONS[assertion_sexp[2]]
|
74
|
+
@output << explain_expected("expected", assertion_sexp[1], msg, assertion_sexp[3])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def explain_expected(expect_msg, expect_sexp, got_msg, got_sexp)
|
79
|
+
width = [expect_msg.size, got_msg.size].max
|
80
|
+
sprintf("%#{width}s: %s\n%#{width}s: %s\n",
|
81
|
+
expect_msg, eval_sexp(expect_sexp),
|
82
|
+
got_msg, eval_sexp(got_sexp))
|
83
|
+
end
|
84
|
+
|
85
|
+
def expression_value_pairs
|
86
|
+
assertion_subexpressions.map { |exp|
|
87
|
+
[exp, eval_in(exp, @env)]
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
def assertion_subexpressions
|
92
|
+
Sorcerer.subexpressions(assertion_sexp).reverse.uniq.reverse
|
93
|
+
end
|
94
|
+
|
95
|
+
def assertion_sexp
|
96
|
+
@assertion_sexp ||= extract_test_expression(Ripper::SexpBuilder.new(source).parse)
|
97
|
+
end
|
98
|
+
|
99
|
+
def source
|
100
|
+
@line_extractor.line(@code_file, @code_line)
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_file_and_line
|
104
|
+
@code_file, @code_line = eval "[__FILE__, __LINE__]", @block.binding
|
105
|
+
@code_line = @code_line.to_i
|
106
|
+
end
|
107
|
+
|
108
|
+
def extract_test_expression(sexp)
|
109
|
+
brace_block = extract_brace_block(sexp)
|
110
|
+
extract_first_statement(brace_block)
|
111
|
+
end
|
112
|
+
|
113
|
+
def extract_brace_block(sexp)
|
114
|
+
unless then_block?(sexp)
|
115
|
+
source = Sorcerer.source(sexp)
|
116
|
+
fail InvalidThenError, "Unexpected code at #{source_line}\n#{source}"
|
117
|
+
end
|
118
|
+
sexp[1][2][2]
|
119
|
+
end
|
120
|
+
|
121
|
+
def then_block?(sexp)
|
122
|
+
sexp.first == :program &&
|
123
|
+
sexp[1].first == :stmts_add &&
|
124
|
+
sexp[1][2].first == :method_add_block &&
|
125
|
+
(sexp[1][2][2].first == :brace_block || sexp[1][2][2].first == :do_block)
|
126
|
+
end
|
127
|
+
|
128
|
+
def extract_first_statement(block_sexp)
|
129
|
+
unless contains_one_statement?(block_sexp)
|
130
|
+
source = Sorcerer.source(block_sexp)
|
131
|
+
fail InvalidThenError, "Multiple statements in Then block at #{source_line}\n#{source}"
|
132
|
+
end
|
133
|
+
extract_statement_from_block(block_sexp)
|
134
|
+
end
|
135
|
+
|
136
|
+
def contains_one_statement?(block_sexp)
|
137
|
+
block_sexp[2].first == :stmts_add &&
|
138
|
+
block_sexp[2][1].first == :stmts_new
|
139
|
+
end
|
140
|
+
|
141
|
+
def extract_statement_from_block(block_sexp)
|
142
|
+
block_sexp[2][2]
|
143
|
+
end
|
144
|
+
|
145
|
+
def eval_sexp(sexp)
|
146
|
+
expr = Sorcerer.source(sexp)
|
147
|
+
eval_in(expr, @env)
|
148
|
+
end
|
149
|
+
|
150
|
+
def eval_in(exp, binding)
|
151
|
+
eval(exp, binding).inspect
|
152
|
+
rescue StandardError => ex
|
153
|
+
EvalErr.new("#{ex.class}: #{ex.message}")
|
154
|
+
end
|
155
|
+
|
156
|
+
WRAP_WIDTH = 20
|
157
|
+
|
158
|
+
def display_pairs(pairs)
|
159
|
+
width = suggest_width(pairs)
|
160
|
+
pairs.each do |x, v|
|
161
|
+
fmt = (v.size > WRAP_WIDTH) ?
|
162
|
+
" %-#{width+2}s\n #{' '*(width+2)} <- %s\n" :
|
163
|
+
" %-#{width+2}s <- %s\n"
|
164
|
+
@output << sprintf(fmt, v, x)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def suggest_width(pairs)
|
169
|
+
pairs.map { |x,v| v.size }.select { |n| n < WRAP_WIDTH }.max || 10
|
170
|
+
end
|
171
|
+
|
172
|
+
def source_line
|
173
|
+
"#{@code_file}:#{@code_line}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
data/lib/rspec/given/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-given
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
5
|
-
prerelease:
|
4
|
+
version: 2.3.0.beta.1
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jim Weirich
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>'
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 1.2.8
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sorcerer
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.3.7
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.3.7
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: bluecloth
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,6 +75,22 @@ dependencies:
|
|
59
75
|
- - ! '>'
|
60
76
|
- !ruby/object:Gem::Version
|
61
77
|
version: 2.4.2
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: ghpreview
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.0.1
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.0.1
|
62
94
|
description: ! 'Given is an RSpec extension that allows explicit definition of the
|
63
95
|
|
64
96
|
pre and post-conditions for code under test.
|
@@ -76,20 +108,25 @@ files:
|
|
76
108
|
- README.md
|
77
109
|
- lib/rspec-given.rb
|
78
110
|
- lib/rspec/given/configure.rb
|
111
|
+
- lib/rspec/given/core.rb
|
79
112
|
- lib/rspec/given/extensions.rb
|
80
113
|
- lib/rspec/given/failure.rb
|
81
114
|
- lib/rspec/given/file_cache.rb
|
82
115
|
- lib/rspec/given/have_failed.rb
|
83
116
|
- lib/rspec/given/line_extractor.rb
|
84
117
|
- lib/rspec/given/module_methods.rb
|
118
|
+
- lib/rspec/given/natural_assertion.rb
|
85
119
|
- lib/rspec/given/rspec1_given.rb
|
86
120
|
- lib/rspec/given/version.rb
|
87
121
|
- lib/rspec/given.rb
|
88
122
|
- examples/example_helper.rb
|
123
|
+
- examples/failing/natural_failing_spec.rb
|
124
|
+
- examples/failing/sample_spec.rb
|
89
125
|
- examples/integration/and_spec.rb
|
90
126
|
- examples/integration/focused_line_spec.rb
|
91
127
|
- examples/integration/given_spec.rb
|
92
128
|
- examples/integration/invariant_spec.rb
|
129
|
+
- examples/integration/then_spec.rb
|
93
130
|
- examples/other/line_example.rb
|
94
131
|
- examples/stack/stack.rb
|
95
132
|
- examples/stack/stack_spec.rb
|
@@ -111,16 +148,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
148
|
requirements:
|
112
149
|
- - ! '>='
|
113
150
|
- !ruby/object:Gem::Version
|
114
|
-
version:
|
115
|
-
segments:
|
116
|
-
- 0
|
117
|
-
hash: 3954842768326946335
|
151
|
+
version: 1.9.2
|
118
152
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
153
|
none: false
|
120
154
|
requirements:
|
121
|
-
- - ! '
|
155
|
+
- - ! '>'
|
122
156
|
- !ruby/object:Gem::Version
|
123
|
-
version:
|
157
|
+
version: 1.3.1
|
124
158
|
requirements: []
|
125
159
|
rubyforge_project: given
|
126
160
|
rubygems_version: 1.8.24
|