├── .gitignore
├── .rspec
├── .travis.yml
├── Gemfile
├── MIT-LICENSE
├── README.md
├── Rakefile
├── TODO
├── doc
├── article
│ └── custom_error_messages.md
└── main.rdoc
├── examples
├── example_helper.rb
├── failing
│ ├── natural_failing_spec.rb
│ └── sample_spec.rb
├── integration
│ ├── and_spec.rb
│ ├── failing
│ │ ├── eval_subexpression_spec.rb
│ │ ├── module_nesting_spec.rb
│ │ ├── oddly_formatted_then.rb
│ │ ├── to_bool_returns_false.rb
│ │ └── undefined_method_spec.rb
│ ├── failing_messages_spec.rb
│ ├── focused_line_spec.rb
│ ├── given_spec.rb
│ ├── invariant_spec.rb
│ └── then_spec.rb
├── loader.rb
├── minitest
│ └── assert_raises_spec.rb
├── minitest_helper.rb
├── other
│ └── line_example.rb
├── stack
│ ├── stack.rb
│ ├── stack_spec.rb
│ └── stack_spec1.rb
└── use_assertions.rb
├── lib
├── given.rb
├── given
│ ├── assertions.rb
│ ├── binary_operation.rb
│ ├── core.rb
│ ├── evaluator.rb
│ ├── ext
│ │ └── numeric.rb
│ ├── extensions.rb
│ ├── failure.rb
│ ├── failure_matcher.rb
│ ├── file_cache.rb
│ ├── fuzzy_number.rb
│ ├── fuzzy_shortcuts.rb
│ ├── line_extractor.rb
│ ├── minitest
│ │ ├── all.rb
│ │ ├── before_extension.rb
│ │ ├── configure.rb
│ │ ├── context_extension.rb
│ │ ├── failure_must_raise.rb
│ │ ├── framework.rb
│ │ └── new_assertions.rb
│ ├── module_methods.rb
│ ├── natural_assertion.rb
│ ├── rspec
│ │ ├── all.rb
│ │ ├── before_extensions.rb
│ │ ├── configure.rb
│ │ ├── framework.rb
│ │ ├── have_failed.rb
│ │ ├── have_failed_212.rb
│ │ ├── have_failed_pre212.rb
│ │ ├── monkey.rb
│ │ └── use_natural_assertions.rb
│ └── version.rb
├── minitest-given.rb
├── minitest
│ └── given.rb
├── rspec-given.rb
└── rspec
│ └── given.rb
├── rakelib
├── bundler_fix.rb
├── gemspec.rake
├── metrics.rake
└── preview.rake
├── spec
├── lib
│ └── given
│ │ ├── assertions_spec.rb
│ │ ├── binary_operation_spec.rb
│ │ ├── evaluator_spec.rb
│ │ ├── ext
│ │ ├── numeric_spec.rb
│ │ └── numeric_specifications.rb
│ │ ├── extensions_spec.rb
│ │ ├── failing_thens_spec.rb
│ │ ├── failure_matcher_spec.rb
│ │ ├── failure_spec.rb
│ │ ├── file_cache_spec.rb
│ │ ├── fuzzy_number_spec.rb
│ │ ├── have_failed_spec.rb
│ │ ├── lexical_purity_spec.rb
│ │ ├── line_extractor_spec.rb
│ │ ├── module_methods_spec.rb
│ │ ├── natural_assertion_spec.rb
│ │ └── options_spec.rb
├── spec_helper.rb
└── support
│ ├── be_booleany.rb
│ ├── failure_and_errors.rb
│ ├── faux_then.rb
│ └── natural_assertion_control.rb
└── support
└── experiments
└── demo.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | .rbx
2 | Gemfile.lock
3 | README
4 | README.old
5 | TAGS
6 | extra
7 | html
8 | pkg
9 | xxx-*
10 | .bundle
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | -Ilib -Ispec -Iexamples --color
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | bundler_args: --without dev
3 | env:
4 | - CI=true
5 | rvm:
6 | - jruby
7 | - 1.9.3
8 | - 2.0.0
9 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'rake'
4 | gem 'rspec', '>= 2.12'
5 | gem 'minitest', '>= 4.3'
6 | gem 'sorcerer', '>= 0.3.7'
7 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011, 2012, 2013 by Jim Weirich
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Given/When/Then for RSpec and Minitest
2 |
3 | | Master |
4 | | :----: |
5 | | [](https://travis-ci.org/jimweirich/rspec-given) |
6 |
7 | Covering rspec-given, minitest-given, and given-core, version 3.5.3.
8 |
9 | rspec-given and minitest-given are extensions to your favorite testing
10 | framework to allow Given/When/Then notation when writing specs.
11 |
12 | # Why Given/When/Then
13 |
14 | RSpec has done a great job of making specifications more readable for
15 | humans. However, I really like the given/when/then nature of Cucumber
16 | stories and would like to follow the same structure in my unit tests.
17 | rspec-given (and now minitest-given) allows a simple given/when/then
18 | structure RSpec specifications.
19 |
20 | ## Status
21 |
22 | * rspec-given and minitest-given are ready for production use.
23 |
24 | ### RSpec/Given
25 |
26 | The rspec-given gem is the original given/when/then extension for
27 | RSpec. It now depends on a given_core gem for the basic functionality
28 | and then adds the RSpec specific code.
29 |
30 | * rspec-given now requires RSpec version 2.12 or better.
31 |
32 | ### Minitest/Given
33 |
34 | A new minitest-given gem allows Given/When/Then notation directly in
35 | Minitest::Spec specifications.
36 |
37 | To use minitest-given, just place the following require at the top of
38 | the file (or in a convenient spec_helper).
39 |
40 | ```ruby
41 | require 'minitest/given'
42 | ```
43 |
44 | All the features of rspec-given are available in minitest-given.
45 |
46 | When switching from RSpec/Given to Minitest/Given, here are some
47 | things to watch out for:
48 |
49 | * You need to use Minitest version 4.3 or better (yes, Minitest 5.x
50 | should work as well).
51 |
52 | * Minitest/Given adds the missing "context" block to Minitest::Spec.
53 |
54 | * Only one before block is allowed in any given Minitest::Spec
55 | describe block. This doesn't effect the number of Givens you are
56 | allowed to use, but it may surprise if you are use to RSpec.
57 |
58 | ### Auto Selecting
59 |
60 | If you use natural assertions exclusively in your specs, it's quite
61 | possible to write specs that run under both RSpec and Minitest::Spec.
62 |
63 | Use this at the start of your spec file:
64 |
65 | ```ruby
66 | if defined?(RSpec)
67 | require 'rspec/given'
68 | else
69 | require 'minitest/autorun'
70 | require 'minitest/given'
71 | end
72 | ```
73 |
74 | See
75 | [stack_spec.rb](https://github.com/jimweirich/rspec-given/blob/minispec/examples/stack/stack_spec.rb)
76 | and
77 | [example_helper.rb](https://github.com/jimweirich/rspec-given/blob/minispec/examples/example_helper.rb)
78 |
79 | ## Installation
80 |
81 | ### If you are using bundler
82 |
83 | Add `rspec-given` (or `minitest-given`) to the `:test` group in the `Gemfile`:
84 |
85 | ```ruby
86 | group :test do
87 | gem 'rspec-given'
88 | end
89 | ```
90 |
91 | ```ruby
92 | group :test do
93 | gem 'minitest-given'
94 | end
95 | ```
96 |
97 | Download and install:
98 |
99 | `$ bundle`
100 |
101 | Then just require `rspec/given` (or `minitest/given`) in the
102 | `spec_helper` of your project and it is ready to go.
103 |
104 | ### If you are not using bundler
105 |
106 | Install the gem:
107 |
108 | `$ gem install rspec-given`
109 |
110 | or
111 |
112 | `$ gem install minitest-given`
113 |
114 | Then just require `rspec/given` (or `minitest/given`) in the
115 | `spec_helper` of your project and it is ready to go.
116 |
117 | ## Example
118 |
119 | Here is a specification written in the rspec-given framework:
120 |
121 | ```ruby
122 | require 'rspec/given'
123 | require 'spec_helper'
124 | require 'stack'
125 |
126 | describe Stack do
127 | def stack_with(initial_contents)
128 | stack = Stack.new
129 | initial_contents.each do |item| stack.push(item) end
130 | stack
131 | end
132 |
133 | Given(:stack) { stack_with(initial_contents) }
134 | Invariant { stack.empty? == (stack.depth == 0) }
135 |
136 | context "with no items" do
137 | Given(:initial_contents) { [] }
138 | Then { stack.depth == 0 }
139 |
140 | context "when pushing" do
141 | When { stack.push(:an_item) }
142 |
143 | Then { stack.depth == 1 }
144 | Then { stack.top == :an_item }
145 | end
146 |
147 | context "when popping" do
148 | When(:result) { stack.pop }
149 | Then { result == Failure(Stack::UnderflowError, /empty/) }
150 | end
151 | end
152 |
153 | context "with one item" do
154 | Given(:initial_contents) { [:an_item] }
155 |
156 | context "when popping" do
157 | When(:pop_result) { stack.pop }
158 |
159 | Then { pop_result == :an_item }
160 | Then { stack.depth == 0 }
161 | end
162 | end
163 |
164 | context "with several items" do
165 | Given(:initial_contents) { [:second_item, :top_item] }
166 | Given!(:original_depth) { stack.depth }
167 |
168 | context "when pushing" do
169 | When { stack.push(:new_item) }
170 |
171 | Then { stack.top == :new_item }
172 | Then { stack.depth == original_depth + 1 }
173 | end
174 |
175 | context "when popping" do
176 | When(:pop_result) { stack.pop }
177 |
178 | Then { pop_result == :top_item }
179 | Then { stack.top == :second_item }
180 | Then { stack.depth == original_depth - 1 }
181 | end
182 | end
183 | end
184 | ```
185 |
186 | Let's talk about the individual statements used in the Given
187 | framework.
188 |
189 | ### Given
190 |
191 | The _Given_ section specifies a starting point, a set of preconditions
192 | that must be true before the code under test is allowed to be run. In
193 | standard test frameworks the preconditions are established with a
194 | combination of setup methods (or :before actions in RSpec) and code in
195 | the test.
196 |
197 | In the example code above the preconditions are started with _Given_
198 | statements. A top level _Given_ (that applies to the entire describe
199 | block) says that one of the preconditions is that there is a stack
200 | with some initial contents.
201 |
202 | Note that initial contents are not specified in the top level describe
203 | block, but are given in each of the nested contexts. By pushing the
204 | definition of "initial_contents" into the nested contexts, we can vary
205 | them as needed for that particular context.
206 |
207 | A precondition in the form "Given(:var) {...}" creates an accessor
208 | method named "var". The accessor is lazily initialized by the code
209 | block. If you want a non-lazy given, use "Given!(:var) {...}".
210 |
211 | A precondition in the form "Given {...}" just executes the code block
212 | for side effects. Since there is no accessor, the code block is
213 | executed immediately (i.e. no lazy evaluation).
214 |
215 | The preconditions are run in order of definition. Nested contexts
216 | will inherit the preconditions from the enclosing context, with outer
217 | preconditions running before inner preconditions.
218 |
219 | #### Given examples:
220 |
221 | ```ruby
222 | Given(:stack) { Stack.new }
223 | ```
224 |
225 | The block for the given clause is lazily run and its value bound to
226 | 'stack' if 'stack' is ever referenced in the test.
227 | The first reference to 'stack' in the specification will cause the
228 | code block to execute. Futher references to 'stack' will reuse the
229 | previously generated value.
230 |
231 | ```ruby
232 | Given!(:original_size) { stack.size }
233 | ```
234 |
235 | The code block is run unconditionally once before each test and the
236 | value of the block is bound to 'original_size'. This form is useful
237 | when you want to record the value of something that might be affected
238 | by the When code.
239 |
240 | ```ruby
241 | Given { stack.clear }
242 | ```
243 |
244 | The block for the given clause is run unconditionally once before each
245 | test. This form of given is used for code that is executed for side
246 | effects.
247 |
248 | ### When
249 |
250 | The _When_ clause specifies the code to be tested ... oops, excuse me
251 | ... specified. After the preconditions in the given section are met,
252 | the when code block is run.
253 |
254 | In general there should not be more than one _When_ clause for a given
255 | direct context. However, a _When_ in an outer context will be run
256 | after all the _Givens_ but before the inner _When_. You can think of
257 | an outer _When_ as setting up additional given state for the inner
258 | _When_.
259 |
260 | E.g.
261 |
262 | ```ruby
263 | context "outer context" do
264 | When { code specified in the outer context }
265 | Then { assert something about the outer context }
266 |
267 | context "inner context" do
268 |
269 | # At this point, the _When_ of the outer context will be run
270 | # before the _When_ of then inner context (but after all the
271 | # _Givens_ of all the contexts). You can think of the outer
272 | # _When_ as a special given for the inner scope.
273 |
274 | When { code specified in the inner context }
275 | Then { assert something about the inner context }
276 | end
277 | end
278 | ```
279 |
280 | #### When examples:
281 |
282 | ```ruby
283 | When { stack.push(:item) }
284 | ```
285 |
286 | The code block is executed once per test. The effect of the _When{}_
287 | block is very similar to _Given{}_. However, When is used to identify
288 | the particular code that is being specified in the current context or
289 | describe block.
290 |
291 | ```ruby
292 | When(:result) { stack.pop }
293 | ```
294 |
295 | The code block is executed once per test and the value of the code
296 | block is bound to 'result'. Use this form when the code under test
297 | returns a value that you wish to interrogate in the _Then_ code.
298 |
299 | If an exception occurs during the execution of the block for the When
300 | clause, the exception is caught and a failure object is bound to
301 | 'result'. The failure can be checked in a then block with the
302 | 'have_failed' matcher.
303 |
304 | The failure object will rethrow the captured exception if anything
305 | other than have_failed matcher is used on the failure object.
306 |
307 | For example, if the stack is empty when it is popped, then it is
308 | reasonable for pop to raise an UnderflowError. This is how you might
309 | specify that behavior:
310 |
311 | ```ruby
312 | When(:result) { stack.pop }
313 | Then { expect(result).to have_failed(UnderflowError, /empty/) }
314 | ```
315 |
316 | The arguments to the 'have_failed' matcher are the same as
317 | those given to the standard RSpec matcher 'raise_error'.
318 |
319 | *NOTE:* Prior to RSpec 3, the .should method worked with the failed
320 | result. In RSpec 3 the .should
method is deprecated and
321 | should not be used with the failed result.
322 |
323 | ### Then
324 |
325 | The _Then_ clauses are the postconditions of the specification. These
326 | then conditions must be true after the code under test (the _When_
327 | clause) is run.
328 |
329 | The code in the block of a _Then_ clause should be a single _should_
330 | assertion. Code in _Then_ clauses should not have any side effects.
331 |
332 | Let me repeat that: _Then_ clauses should not have any side
333 | effects! _Then_ clauses with side effects are erroneous. _Then_
334 | clauses need to be idempotent, so that running them once, twice, a
335 | hundred times, or never does not change the state of the program. (The
336 | same is true of _And_ and _Invariant_ clauses).
337 |
338 | In RSpec terms, a _Then_ clause forms a RSpec Example that runs in the
339 | context of an Example Group (defined by a describe or context clause).
340 |
341 | Each Example Group must have at least one _Then_ clause, otherwise
342 | there will be no examples to be run for that group. If all the
343 | assertions in an example group are done via Invariants, then the group
344 | should use an empty _Then_ clause, like this:
345 |
346 | ```ruby
347 | Then { }
348 | ```
349 |
350 | #### Then examples:
351 |
352 | ```ruby
353 | Then { stack.empty? }
354 | ```
355 |
356 | After the related block for the _When_ clause is run, the stack should
357 | be empty. If it is not empty, the test will fail.
358 |
359 | ### And
360 |
361 | The _And_ clause is similar to _Then_, but does not form its own RSpec
362 | example. This means that _And_ clauses reuse the setup from a sibling
363 | _Then_ clause. Using a single _Then_ and multiple _And_ clauses in an
364 | example group means the setup for that group is run only once (for the
365 | _Then_ clause) and reused for all the _And_ clauses. This can be a
366 | significant speed savings where the setup for an example group is
367 | expensive.
368 |
369 | Some things to keep in mind about _And_ clauses:
370 |
371 | * There must be at least one _Then_ in the example group and it must
372 | be declared before the _And_ clauses. Forgetting the _Then_ clause
373 | is an error.
374 |
375 | * The code in the _And_ clause is run immediately after the first
376 | (executed) _Then_ of an example group.
377 |
378 | * An assertion failure in a _Then_ clause or an _And_ clause will
379 | cause all the subsequent _And_ clauses to be skipped.
380 |
381 | * Since _And_ clauses do not form their own RSpec examples, they are
382 | not represented in the formatted output of RSpec. That means _And_
383 | clauses do not produce dots in the Progress format, nor do they
384 | appear in the documentation, html or textmate formats (options
385 | -fhtml, -fdoc, or -ftextmate).
386 |
387 | * Like _Then_ clauses, _And_ clauses must be idempotent. That means
388 | they should not execute any code that changes global program state.
389 | (See the section on the _Then_ clause).
390 |
391 | The choice to use an _And_ clause is primarily a speed consideration.
392 | If an example group has expensive setup and there are a lot of _Then_
393 | clauses, then choosing to make some of the _Then_ clauses into _And_
394 | clauses will speed up the spec. Otherwise it is probably better to
395 | stick with _Then_ clauses.
396 |
397 | #### Then/And examples:
398 |
399 | ```ruby
400 | Then { pop_result == :top_item } # Required
401 | And { stack.top == :second_item } # No Setup rerun
402 | And { stack.depth == original_depth - 1 } # ... for these
403 | ```
404 |
405 | ### Invariant
406 |
407 | The _Invariant_ clause is a new idea that doesn't have an analog in
408 | RSpec or Test::Unit. The invariant allows you specify things that must
409 | always be true in the scope of the invariant. In the stack example, the method
410 | empty? is defined in term of size.
411 |
412 | ```ruby
413 | Invariant { stack.empty? == (stack.depth == 0) }
414 | ```
415 |
416 | This invariant states that empty?
is true if and only if
417 | the stack depth is zero, and that assertion is checked at every _Then_
418 | clause that is in the same scope.
419 |
420 | You can conceptually think of an _Invariant_ clause as a _Then_ block
421 | that automatically gets added to every _Then_ within its scope.
422 | Invariants nested within a context only apply to the _Then_ clauses
423 | that are in the scope of that context.
424 |
425 | Invariants that reference a _Given_ precondition accessor must only be
426 | used in contexts that define that accessor.
427 |
428 | Notes:
429 |
430 | * Since Invariants do not form their own RSpec example, they are not
431 | represented in the RSpec formatted output (e.g. the '--format html'
432 | option).
433 |
434 | ## Execution Ordering
435 |
436 | When running the test for a specific _Then_ clause, the following will
437 | be true:
438 |
439 | * The non-lazy _Given_ clauses will be run in the order that they are
440 | specified, from the outermost scope to the innermost scope
441 | containing the _Then_. (The lazy _Given_ clauses will be run upon
442 | demand).
443 |
444 | * All of the _Given_ clauses in all of the relevant scopes will run
445 | before the first (outermost) _When_ clause in those same scopes.
446 | That means that the _When_ code can assume that the givens have been
447 | established, even if the givens are in a more nested scope than the
448 | When.
449 |
450 | * _When_ clauses and RSpec _before_ blocks will be executed in the
451 | order that they are specified, from the outermost block to the
452 | innermost block. This makes _before_ blocks an excellent choice when
453 | writing narrative tests to specify actions that happen between the
454 | "whens" of a narrative-style test.
455 |
456 | Note that the ordering between _Given_ clauses and _before_ blocks are
457 | not strongly specified. Hoisting a _When_ clause out of an inner scope
458 | to an outer scope may change the order of execution between related
459 | _Given_ clauses and any _before_ blocks (hoisting the _When_ clause
460 | might cause the related _Given_ clauses to possibly run earlier).
461 | Because of this, do not split order dependent code between _Given_
462 | clauses and _before_ blocks.
463 |
464 | ## Natural Assertions
465 |
466 | RSpec/Given now supports the use of "natural assertions" in _Then_,
467 | _And_, and _Invariant_ blocks. Natural assertions are just Ruby
468 | conditionals, without the _should_ or _expect_ methods that RSpec
469 | provides.
470 |
471 | When using natural assertions, the value of the _Then_ expression
472 | determines the pass/fail state of the test. If the expression is
473 | true, the test passes. If the test is false, the test fails.
474 |
475 | If the _Then_ expression executes an RSpec (or MiniTest) assertion
476 | (e.g. uses should
, expect
or
477 | assert_xxx
), then the true/false value will be ignored.
478 | This allows natural assertions and regular assertions clauses to be
479 | intermixed at will.
480 |
481 | In addition, if the value of a _Then_ class returns an object that
482 | responds to to_bool
, then to_bool
will be
483 | called and the return value of that will be used to determine if the
484 | test passed or failed.
485 |
486 | Here are the Then/And examples showing natural assertions:
487 |
488 | ### Using Natural Assertions
489 |
490 | ```ruby
491 | Then { stack.top == :second_item }
492 | Then { stack.depth == original_depth - 1 }
493 | Then { result == Failure(Stack::UnderflowError, /empty/) }
494 | ```
495 |
496 | ### Using RSpec expect().to
497 |
498 | ```ruby
499 | Then { expect(stack.top).to eq(:second_item) }
500 | Then { expect(stack.depth).to eq(original_depth - 1) }
501 | Then { expect(result).to have_failed(Stack::UnderflowError, /empty/) }
502 | ```
503 |
504 | ### Using Minitest asserts
505 |
506 | ```ruby
507 | Then { assert_equal :second_item, stack.top }
508 | Then { assert_equal original_depth - 1, stack.depth }
509 | Then {
510 | assert_raises(Stack::UnderflowError, /empty/) do
511 | result.call()
512 | end
513 | }
514 | ```
515 |
516 | ### Using Minitest expectations
517 |
518 | ```ruby
519 | Then { stack.top.must_equal :second_item }
520 | Then { stack.depth.must_equal original_depth - 1}
521 | Then { result.must_raise(Stack::UnderflowError, /empty/) }
522 | ```
523 |
524 | ### Disabling Natural Assertions
525 |
526 | Natural assertions may be disabled, either globally or on a per
527 | context basis. See the **configuration** section below to see how to
528 | disable natural assertions project wide.
529 |
530 | Here's a heads up: If you use natural assertions, but configure Given
531 | to disable them, then all your specs will mysteriously pass. This is
532 | why the **red** part of _Red/Green/Refactor_ is so important.
533 |
534 | ### Failure Messages with Natural Assertions
535 |
536 | Since natural assertions do not depend upon matchers, you don't get
537 | customized error messages from them. What you _do_ get is a complete
538 | analsysis of the expression that failed.
539 |
540 | For example, given the following failing specification:
541 |
542 | ```ruby
543 | Given.use_natural_assertions
544 |
545 | describe "Natural Assertions" do
546 | Given(:foo) { 1 }
547 | Given(:bar) { 2 }
548 | Then { foo + bar == 2 }
549 | end
550 | ```
551 |
552 | You would get:
553 |
554 | ```
555 | 1) Natural Assertions
556 | Failure/Error: Then { foo + bar == 2 }
557 | Then expression failed at /Users/jim/working/git/rspec-given/examples/failing/sample_spec.rb:6
558 | expected: 3
559 | to equal: 2
560 | false <- foo + bar == 2
561 | 3 <- foo + bar
562 | 1 <- foo
563 | 2 <- bar
564 | # ./examples/failing/sample_spec.rb:6:in `block in Then'
565 | ```
566 |
567 | Notice how the failing expression "foo+bar == 2
" was
568 | broken down into subexpressions and values for each subexpression.
569 | This gives you all the information you need to figure out exactly what
570 | part of the expression is causing the failure.
571 |
572 | Natural assertions will give additional information (e.g. "expected:
573 | 3 to equal: 2") for top level expressions involving any of the
574 | comparison operators (==, !=, <, <=, >, >=) or matching operators (=~,
575 | !~).
576 |
577 | ### Checking for exceptions with Natural Assertions
578 |
579 | If you wish to see if the result of a _When_ clause is an exception,
580 | you can use the following:
581 |
582 | ```ruby
583 | When(:result) { stack.pop }
584 | Then { result == Failure(UnderflowError, /empty/) }
585 | ```
586 |
587 | The Failure()
method accepts the same arguments as
588 | have_failed
and raise_error
.
589 |
590 | ### Caveats on Natural Assertions
591 |
592 | Keep the following in mind when using natural assertions.
593 |
594 | * Only a single expression/assertion per _Then_. The single expression
595 | of the _Then_ block will be considered when determining pass/fail
596 | for the assertion. If you _want_ to express a complex condition for
597 | the _Then_, you need to use ||, && or some other logical operation
598 | to join the conditions into a single expression (and the failure
599 | message will break down the values for each part).
600 |
601 | * Then clauses need be **idempotent**. This is true in general, but it
602 | is particularly important for natural assertions to obey this
603 | restriction. This means that assertions in a Then clause should not
604 | change anything. Since the Natural Assertion error message contains
605 | the values of all the subexpressions, the expression and its
606 | subexpressions will be evaluated multiple times. If the Then clause
607 | is not idempotent, you will get changing answers as the
608 | subexpressions are evaluated.
609 |
610 | That last point is important. If you write code like this:
611 |
612 | ```ruby
613 | # DO NOT WRITE CODE LIKE THIS
614 | context "Incorrect non-idempotent conditions" do
615 | Given(:ary) { [1, 2, 3] }
616 | Then { ary.delete(1) == nil }
617 | end
618 | ```
619 |
620 | Then the assertion will fail (because ary.delete(1)
will
621 | initially return 1). But when the error message is formated, the
622 | system reports that ary.delete(1)
returns nil. You will
623 | scratch your head over that for a good while.
624 |
625 | Instead, move the state changing code into a _When(:result)_ block, then
626 | assert what you need to about :result. Something
627 | like this is good:
628 |
629 | ```ruby
630 | context "Correct idempotent conditions" do
631 | Given(:ary) { [1, 2, 3] }
632 | When(:result) { ary.delete(1) }
633 | Then { result == nil }
634 | end
635 | ```
636 |
637 | It is good to note that non-idempotent assertions will also cause
638 | problems with And and Invariant clauses.
639 |
640 | ### Mixing Natural Assertions and RSpec Assertions
641 |
642 | Natural assertions, RSpec should assertions and Minitest assertions
643 | can be intermixed in a single test suite, even within a single
644 | context.
645 |
646 | ```ruby
647 | context "Outer" do
648 | context "Inner" do
649 | Then { a == b } # Natural Assertions
650 | Then { a.should == b } # Deprecated RSpec style
651 | Then { expect(a).to eq(b) } # RSpec style
652 | Then { assert_equal b, a } # Minitest style
653 | Then { a.must_equal b } # Minitest style
654 | end
655 |
656 | context "Disabled" do
657 | use_natural_assertions false
658 | end
659 | end
660 | ```
661 |
662 | Both the _Outer_ and _Inner_ contexts will use natural assertions. The
663 | _Disabled_ context overrides the setting inherited from _Outer_ and
664 | will not process natural assertions.
665 |
666 | See the **configuration** section below to see how to disable natural
667 | assertions project wide.
668 |
669 | ### Matchers and Natural Assertions
670 |
671 | In RSpec, matchers are used to provide nice, readable error messages
672 | when an assertion is not met. Natural assertions provide
673 | self-explanatory failure messages for most things without requiring
674 | any special matchers from the programmer.
675 |
676 | In the rare case that some extra information would be helpful, it is
677 | useful to create special objects that respond to the == operator.
678 |
679 | #### Asserting Nearly Equal with Fuzzy Numbers
680 |
681 | Operations on floating point numbers rarely create numbers that are
682 | exactly equal, therefore it is useful to assert that two floating
683 | point numbers are nearly equal. We do that by creating a fuzzy number
684 | that has a looser interpretation of what it means to be equal.
685 |
686 | For example, the following asserts that the square root of 10 is about
687 | 3.1523 with an accuracy of 1 percent.
688 |
689 | ```ruby
690 | Then { Math.sqrt(10) == about(3.1623).percent(1) }
691 | ```
692 |
693 | As long as the real value of Math.sqrt(10)
is within plus
694 | or minus 1% of 3.1623 (i.e. 3.1623 +/- 0.031623), then the assertion
695 | will pass.
696 |
697 | There are several ways of creating fuzzy numbers:
698 |
699 | * about(n).delta(d)
-- A fuzzy number matching the range
700 | (n-d)..(n+d)
701 |
702 | * about(n).percent(p)
-- A fuzzy number matching the
703 | range (n-(n*p/100)) .. (n+(n*p/100))
704 |
705 | * about(n).epsilon(neps)
-- A fuzzy number matching the
706 | range (n-(neps*e)) .. (n+(neps*e)), where e is the difference
707 | between 1.0 and the next smallest floating point number.
708 |
709 | * about(n)
-- Same as about(n).epsilon(10)
.
710 |
711 | When the file given/fuzzy_shortcuts
is required,
712 | the following unicode shortcut methods are added to Numeric to create
713 | fuzzy numbers.
714 |
715 | * n.±(del)
is the same as about(n).delta(del)
716 |
717 | * n.‰(percentage)
is the same as about(n).percent(percentage)
718 |
719 | * n.€(neps)
is the same as about(n).epsilon(neps)
720 |
721 | * n.±
, n.‰
, and n.€
are all
722 | the same as about(n)
723 |
724 | #### Detecting Exceptions
725 |
726 | The RSpec matcher used for detecting exceptions will work with natural
727 | assertions out of the box. Just check for equality against the
728 | Failure()
method return value.
729 |
730 | For example, the following two Then clauses are equivalent:
731 |
732 | ```ruby
733 | # Using an RSpec matcher
734 | Then { expect(result).to have_failed(StandardError, /message/) }
735 |
736 | # Using natural assertions
737 | Then { result == Failure(StandardError, /message/) }
738 | ```
739 |
740 | ### Processing Natural Assertions
741 |
742 | When natural assertions are enabled, they are only used if all of the
743 | following are true:
744 |
745 | 1. The block does not throw an RSpec assertion failure (or any other
746 | exception for that matter).
747 |
748 | 1. The block returns false (blocks that return true pass the
749 | assertion and don't need a failure message).
750 |
751 | 1. The block does not use the native frameworks assertions or
752 | expectations (e.g. RSpec's _should_ or _expect_ methods, or
753 | Minitest's _assert\_xxx_ or _must\_xxx_ methods).
754 |
755 | Detecting that last point (the use of _should_ and _expect_) is done
756 | by modifying the RSpec runtime to report uses of _should_ and
757 | _expect_.
758 |
759 | ### Platform Support
760 |
761 | Given uses the Ripper library to parse the source lines and failing
762 | conditions to find all the sub-expression values upon a failure.
763 | Currently Ripper is not supported on Rubinius and versions of JRuby
764 | prior to JRuby-1.7.5.
765 |
766 | If you want to use a version of Ruby that does not support Ripper,
767 | then natural assertions will disabled. In addition, you should also
768 | disable source caching in the configuration (see the configuration
769 | section below).
770 |
771 | ### Non-Spec Assertions
772 |
773 | Given also provides three assertions meant to be used in
774 | non-test/non-spec code. For example, here is a square root function
775 | decked out with pre and post-condition assertions.
776 |
777 | ```ruby
778 | require 'given/assertions'
779 | require 'given/fuzzy_number'
780 |
781 | include Given::Assertions
782 | include Given::Fuzzy
783 |
784 | def sqrt(n)
785 | Precondition { n >= 0 }
786 | result = Math.sqrt(n)
787 | Postcondition { result ** 2 == about(n) }
788 | result
789 | end
790 | ```
791 |
792 | To use the non-testing assertions, you need to require the
793 | 'given/assertions' file and then include the
794 | Given::Assertions
module into what ever class is using
795 | the
796 | Precondition
/Postcondition
/Assert
797 | methods. The code block for these assertions should always be a
798 | regular Ruby true/false value (the should
and
799 | expect
methods from RSpec are not available).
800 |
801 | Note that this example also uses the fuzzy number matching, but that
802 | is not required for the assertions themselves.
803 |
804 | The assertion methods are:
805 |
806 | * Precondition { bool }
-- If the block evaluates to
807 | false (or nil), throw a Given::Assertions::PreconditionError.
808 |
809 | * Postcondition { bool }
-- If the block evaluates to
810 | false (or nil), throw a Given::Assertions::PostconditionError.
811 |
812 | * Assert { bool }
-- If the block evaluates to
813 | false (or nil), throw a Given::Assertions::AssertError.
814 |
815 | Both PreconditionError and PostconditionError are subclasses of
816 | AssertError.
817 |
818 | You can disable assertion checking with one of the following commands:
819 |
820 | * Given::Assertions.enable_preconditions bool
--
821 | Enable/Disable precondition assertions.
822 | (default to enable)
823 |
824 | * Given::Assertions.enable_postconditions bool
--
825 | Enable/Disable postcondition assertions.
826 | (default to enable)
827 |
828 | * Given::Assertions.enable_asserts bool
--
829 | Enable/Disable assert assertions. (default to enable)
830 |
831 | * Given::Assertions.enable_all bool
--
832 | Enable/Disable all assertions with a single command.
833 | (default to enable)
834 |
835 | ### Further Reading
836 |
837 | Natural assertions were inspired by the [wrong assertion
838 | library](http://rubygems.org/gems/wrong) by [Alex
839 | Chaffee](http://rubygems.org/profiles/alexch) and [Steve
840 | Conover](http://rubygems.org/profiles/sconoversf).
841 |
842 | ## Configuration
843 |
844 | If the RSpec format option document, html or textmate is chosen,
845 | RSpec/Given will automatically add additional source code information to
846 | the examples to produce better looking output. If you don't care about
847 | the pretty output and wish to disable source code caching
848 | unconditionally, then add the following line to your spec helper file:
849 |
850 | ```ruby
851 | Given.source_caching_disabled = true
852 | ```
853 |
854 | Natural assertions are enabled by default. To globally configure
855 | natural assertions, add one of the following lines to your spec_helper
856 | file:
857 |
858 | ```ruby
859 | Given.use_natural_assertions # Enable natural assertions
860 | Given.use_natural_assertions true # Same as above
861 | Given.use_natural_assertions false # Disable natural assertions
862 | Given.use_natural_assertions :always # Always process natural assertions
863 | # ... even when should/expect are detected
864 | ```
865 |
866 | # License
867 |
868 | rspec-given, minitest-given and given_core are available under the MIT
869 | License. See the MIT-LICENSE file in the source distribution.
870 |
871 | # History
872 |
873 | * Version 3.5.4
874 |
875 | * Accommodate the name change on RSpec's Pending exception.
876 |
877 | * Version 3.5.3
878 |
879 | * source_caching_disabled now hard defaults to false, rather than
880 | attempting to guess the default from the formatters.
881 |
882 | * Version 3.5.0
883 |
884 | * Use Ripper to determine complete subexpressions (rather than
885 | relying on finicky indentation rules.
886 |
887 | * Version 3.4.0
888 |
889 | * Bare failure objects in Then clauses will now propagate their
890 | captured failure (added to_bool
to failure object).
891 |
892 | * Version 3.3.0
893 |
894 | * Add support for to_bool
.
895 | * Restrict length of inspect strings printed to 2000 characters.
896 |
897 | * Version 3.2.0
898 |
899 | * Add support for RSpec 3 beta
900 |
901 | * Version 3.1.0
902 |
903 | * Add support for Precondition/Postcondition/Assert in non-spec
904 | code.
905 |
906 | * Version 3.0.1
907 |
908 | * Add support for the === operation in natural assertions.
909 |
910 | * Version 3.0.0
911 |
912 | * Support for minitest added.
913 |
914 | * Introduced gem given\_core to contain the common logic between the
915 | RSpec and Minitest versions. Both the rspec-given gem and the
916 | minitest-given gem have a dependency on given\_core.
917 |
918 | * Natural assertions are now enabled by default.
919 |
920 | * Version 2.4.4
921 |
922 | * Support for RSpec 2.13 added.
923 |
924 | * Version 2.4.3
925 |
926 | * Better natural assertion messages when dealing with multi-line
927 | output.
928 |
929 | * Version 2.4.2
930 |
931 | * Minor adjustment to natural assertion error messages to better
932 | handle multi-line values.
933 |
934 | * Remove flog, flay and other development tools from the bundle and
935 | gemspec. The Rakefile was updated to suggest installing them if
936 | they are not there.
937 |
938 | * Version 2.4.1
939 |
940 | * Fix bug where constants from nested modules were not properly
941 | accessed.
942 |
943 | * Version 2.4.0
944 |
945 | * Add fuzzy number helper methods (with unicode method shortcuts).
946 |
947 | * Fix bug caused by blank lines in Thens.
948 |
949 | # Links
950 |
951 | * Github: [https://github.com/jimweirich/rspec-given](https://github.com/jimweirich/rspec-given)
952 | * Clone URL: git://github.com/jimweirich/rspec-given.git
953 | * Bug/Issue Reporting: [https://github.com/jimweirich/rspec-given/issues](https://github.com/jimweirich/rspec-given/issues)
954 | * Continuous Integration: [http://travis-ci.org/#!/jimweirich/rspec-given](http://travis-ci.org/#!/jimweirich/rspec-given)
955 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/ruby -wKU
2 |
3 | require 'rake/clean'
4 | require './lib/given/version'
5 | require './lib/given/module_methods'
6 |
7 | CLEAN.include("pkg/rspec-given-*").exclude("pkg/*.gem")
8 | CLOBBER.include("*.gemspec", "html", "README", "README.old")
9 |
10 | # README Formatting --------------------------------------------------
11 |
12 | task :default => :examples
13 |
14 | def version
15 | Given::VERSION
16 | end
17 |
18 | def tag_name
19 | "rspec-given-#{version}"
20 | end
21 |
22 | def tagged?
23 | `git tag`.split.include?(tag_name)
24 | end
25 |
26 | def git_clean?
27 | sh "git status | grep 'nothing to commit'", :verbose => false do |status|
28 | return status
29 | end
30 | end
31 |
32 | desc "Display the current version tag"
33 | task :version do
34 | puts tag_name
35 | end
36 |
37 | desc "Tag the current commit with #{tag_name}"
38 | task :tag do
39 | fail "Cannot tag, project directory is not clean" unless git_clean?
40 | fail "Cannot tag, #{tag_name} already exists." if tagged?
41 | sh "git tag #{tag_name}"
42 | end
43 |
44 | desc "Publish the gems"
45 | task :publish_gems => [:clobber, :gem] do
46 | FileList['pkg/*.gem'].each do |gemname|
47 | sh "gem push #{gemname}"
48 | end
49 | end
50 |
51 | # Running examples ---------------------------------------------------
52 |
53 | desc "Run all the examples"
54 | task :examples => [:specs, :rs_examples, :mt_examples]
55 |
56 | desc "Run the specs"
57 | task :specs do
58 | puts "Running specs"
59 | sh "rspec spec"
60 | end
61 |
62 | EXAMPLES = FileList['examples/**/*_spec.rb', 'examples/use_assertions.rb'].
63 | exclude('examples/failing/*.rb').
64 | exclude('examples/minitest/*.rb').
65 | exclude('examples/integration/failing/*.rb')
66 |
67 | MT_EXAMPLES = FileList['examples/minitest/**/*_spec.rb']
68 |
69 | unless Given::NATURAL_ASSERTIONS_SUPPORTED
70 | EXAMPLES.exclude("examples/stack/*.rb")
71 | end
72 |
73 | FAILING_EXAMPLES = FileList['examples/failing/**/*_spec.rb']
74 |
75 | desc "Run the RSpec specs and examples"
76 | task :rs => [:specs, :rs_examples]
77 |
78 | desc "Run the Minitest tests and examples"
79 | task :mt => [:specs, :mt_examples]
80 |
81 | desc "Run the examples in RSpec 2"
82 | task :rs_examples => [:verify_rspec2] do
83 | puts "Running examples (with RSpec2)"
84 | sh "rspec #{EXAMPLES}"
85 | end
86 |
87 | desc "Run the examples in Minitest"
88 | task :mt_examples do
89 | puts "Running examples (with Minitest)"
90 | sh "ruby -Ilib:examples examples/loader.rb #{EXAMPLES} #{MT_EXAMPLES}"
91 | end
92 |
93 | desc "Run failing examples"
94 | task :failing => [:verify_rspec2] do
95 | puts "Running failing examples (with RSpec2)"
96 | sh "rspec #{FAILING_EXAMPLES}"
97 | end
98 |
99 | task :verify_rspec1 do
100 | sh "type spec >/dev/null 2>&1", :verbose => false do |status|
101 | fail "You need to install RSpec 1 in order to test against it." unless status
102 | end
103 | end
104 |
105 | task :verify_rspec2 do
106 | sh "type rspec >/dev/null 2>&1", :verbose => false do |status|
107 | fail "You need to install RSpec 2 in order to test against it." unless status
108 | end
109 | end
110 |
111 | desc "Check all files load properly when independenly loaded."
112 | task :load_check do
113 | SRC_FILES = FileList['lib/given/*.rb'].exclude(%r(rspec1))
114 | SRC_FILES.each do |fn|
115 | sh %{ruby -Ilib -e 'load "#{fn}"'}
116 | end
117 | end
118 |
119 | # Formatting the README ----------------------------------------------
120 |
121 | directory 'html'
122 |
123 | desc "Display the README file"
124 | task :readme => ["README.md"] do
125 | Bundler.with_clean_env do
126 | sh "ghpreview README.md"
127 | end
128 | end
129 |
130 | desc "Generate an RDoc README"
131 | file "README.md" => ["examples/stack/stack_spec.rb", "lib/given/version.rb"] do
132 | open("README.md") do |ins|
133 | open("README.tmp", "w") do |outs|
134 | state = :copy
135 | while line = ins.gets
136 | case state
137 | when :copy
138 | if line =~ /rspec-given, minitest-given, and given-core, version +\d+(\.(\d+|beta))+/i
139 | line.gsub!(/version +\d+(\.(\d+|beta))+/i, "version #{Given::VERSION}")
140 | outs.puts line
141 | elsif line =~ /^
/ 142 | state = :insert 143 | else 144 | outs.puts line 145 | end 146 | when :insert 147 | outs.puts "" 148 | outs.puts open("examples/stack/stack_spec.rb") { |codes| codes.read } 149 | outs.puts "" 150 | state = :skip 151 | when :skip 152 | state = :copy2 if line =~ /^<\/pre>/ 153 | when :copy2 154 | outs.puts line 155 | end 156 | end 157 | end 158 | end 159 | mv "README.md", "README.old" 160 | mv "README.tmp", "README.md" 161 | end 162 | 163 | 164 | # RDoc --------------------------------------------------------------- 165 | begin 166 | require 'rdoc/task' 167 | if RDoc::VERSION > "2.4.2" 168 | RDOC_ENABLED = true 169 | else 170 | puts "Version of RDoc is too old, please gem install a later version" 171 | RDOC_ENABLED = false 172 | end 173 | rescue LoadError => ex 174 | RDOC_ENABLED = false 175 | end 176 | 177 | begin 178 | require 'darkfish-rdoc' 179 | DARKFISH_ENABLED = true 180 | rescue LoadError => ex 181 | DARKFISH_ENABLED = false 182 | end 183 | 184 | if RDOC_ENABLED 185 | def md_to_rdoc(infile, outfile) 186 | open(infile) do |ins| 187 | open(outfile, "w") do |outs| 188 | state = :copy 189 | while line = ins.gets 190 | case state 191 | when :ignore 192 | if line =~ /^-->/ 193 | state = :copy 194 | end 195 | when :pre 196 | if line =~ /^<\/pre>/ 197 | state = :copy 198 | else 199 | outs.puts " #{line}" 200 | end 201 | when :copy 202 | if line =~ /^