├── .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 | | [![Master Build Status](https://secure.travis-ci.org/jimweirich/rspec-given.png?branch=master)](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 =~ /^