Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Rewrite cucumber features.

The old cukes were a mess:

- Lots of duplication and inconsistencies.
- Functioned poorly as documentation.
- Didn't highlight the new syntax well.
- Didn't really cover all of rspec-mocks' features.

The new ones were structurd specifically with documentation in mind.
The specs are meant more for regression coverage. We've limited the
cukes to just things people may actually want to read as part of
docs.

I've also reworked the test unit cuke as a minitest cuke.

For reference, here's the before/after from cucumber:

Before:

77 scenarios (77 passed)
276 steps (276 passed)
0m48.014s

After:

89 scenarios (89 passed)
328 steps (328 passed)
0m57.844s

Fixes #591.
  • Loading branch information...
commit 8338a78be01e5638c0758b42c70377d571b0cdc7 1 parent 0ecaa32
@myronmarston myronmarston authored
Showing with 2,139 additions and 1,816 deletions.
  1. +0 −1  Gemfile
  2. +35 −25 features/.nav
  3. +47 −46 features/README.md
  4. +0 −17 features/Scope.md
  5. +0 −27 features/argument_matchers/README.md
  6. +0 −31 features/argument_matchers/explicit.feature
  7. +0 −85 features/argument_matchers/general_matchers.feature
  8. +0 −26 features/argument_matchers/type_matchers.feature
  9. +35 −0 features/basics/allowing_messages.feature
  10. +73 −0 features/basics/expecting_messages.feature
  11. +35 −0 features/basics/null_object_doubles.feature
  12. +70 −0 features/basics/partial_test_doubles.feature
  13. +99 −0 features/basics/scope.feature
  14. +144 −0 features/basics/spies.feature
  15. +41 −0 features/basics/test_doubles.feature
  16. +12 −0 features/configuring_responses/README.md
  17. +131 −0 features/configuring_responses/block_implementation.feature
  18. +52 −0 features/configuring_responses/calling_the_original_implementation.feature
  19. +28 −0 features/configuring_responses/raising_an_error.feature
  20. +52 −0 features/configuring_responses/returning_a_value.feature
  21. +36 −0 features/configuring_responses/throwing.feature
  22. +76 −0 features/configuring_responses/yielding.feature
  23. +0 −75 features/message_expectations/README.md
  24. +0 −26 features/message_expectations/allow_any_instance_of.feature
  25. +0 −43 features/message_expectations/any_instance.feature
  26. +0 −55 features/message_expectations/block_local_expectations.feature.pending
  27. +0 −23 features/message_expectations/call_original.feature
  28. +0 −107 features/message_expectations/expect_message_using_expect.feature
  29. +0 −118 features/message_expectations/expect_message_using_should_receive.feature
  30. +0 −49 features/message_expectations/message_chains_using_expect.feature
  31. +0 −209 features/message_expectations/receive_counts.feature
  32. +0 −50 features/message_expectations/warn_when_expectation_is_set_on_nil.feature
  33. +0 −77 features/method_stubs/README.md
  34. +0 −136 features/method_stubs/allow_any_instance_of.feature
  35. +0 −40 features/method_stubs/as_null_object.feature
  36. +0 −51 features/method_stubs/receive_message_chain.feature
  37. +0 −44 features/method_stubs/simple_return_value_with_allow.feature
  38. +0 −64 features/method_stubs/simple_return_value_with_stub.feature
  39. +0 −48 features/method_stubs/stub_implementation.feature
  40. +0 −51 features/method_stubs/to_ary.feature
  41. +9 −13 features/mutating_constants/README.md
  42. +4 −4 features/mutating_constants/{hiding_defined_constant.feature → hide_defined_constant.feature}
  43. +22 −0 features/mutating_constants/hide_undefined_constant.feature
  44. +5 −6 features/mutating_constants/stub_defined_constant.feature
  45. +6 −6 features/mutating_constants/stub_undefined_constant.feature
  46. +35 −0 features/old_syntax/README.md
  47. +105 −0 features/old_syntax/any_instance.feature
  48. +90 −0 features/old_syntax/should_receive.feature
  49. +51 −0 features/old_syntax/stub.feature
  50. +69 −0 features/old_syntax/stub_chain.feature
  51. +43 −0 features/old_syntax/unstub.feature
  52. +80 −0 features/outside_rspec/minitest.feature
  53. +10 −10 features/outside_rspec/standalone.feature
  54. +6 −0 features/setting_constraints/README.md
  55. +97 −0 features/setting_constraints/matching_arguments.feature
  56. +63 −0 features/setting_constraints/message_order.feature
  57. +189 −0 features/setting_constraints/receive_counts.feature
  58. +0 −34 features/spies/spy_partial_mock_method.feature
  59. +0 −76 features/spies/spy_pure_mock_method.feature
  60. +0 −18 features/spies/spy_unstubbed_method.feature
  61. +15 −5 features/step_definitions/additional_cli_steps.rb
  62. +24 −0 features/support/disallow_certain_apis.rb
  63. +0 −54 features/test_frameworks/test_unit.feature
  64. +11 −15 features/verifying_doubles/README.md
  65. +8 −11 features/verifying_doubles/class_doubles.feature
  66. +8 −10 features/verifying_doubles/dynamic_classes.feature
  67. +6 −8 features/verifying_doubles/instance_doubles.feature
  68. +13 −16 features/verifying_doubles/object_doubles.feature
  69. +5 −5 features/verifying_doubles/partial_doubles.feature
  70. +3 −0  features/working_with_legacy_code/README.md
  71. +115 −0 features/working_with_legacy_code/any_instance.feature
  72. +79 −0 features/working_with_legacy_code/message_chains.feature
  73. +2 −1  rspec-mocks.gemspec
View
1  Gemfile
@@ -27,7 +27,6 @@ end
platforms :rbx do
gem 'rubysl'
- gem 'rubysl-test-unit'
end
eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom')
View
60 features/.nav
@@ -1,33 +1,43 @@
-- Upgrade.md
-- Scope.md
-- Changelog.md
-- method_stubs:
- - simple_return_value.feature
- - stub_implementation.feature
- - stub_chain.feature
- - stub_with_arguments.feature
- - any_instance.feature
- - as_null_object.feature
- - to_ary.feature
-- message_expectations:
- - expect_message.feature
- - any_instance.feature
- - block_local_expectations.feature.pending
- - warn_when_expectation_is_set_on_nil.feature
-- argument_matchers:
- - explicit.feature
- - general_matchers.feature
- - type_matchers.feature
-- mutating_constants:
- - stub_defined_constant.feature
- - stub_undefined_constant.feature
- - hiding_defined_constant.feature
+- basics:
+ - scope.feature
+ - test_doubles.feature
+ - allowing_messages.feature
+ - expecting_messages.feature
+ - partial_test_doubles.feature
+ - null_object_doubles.feature
+ - spies.feature
- verifying_doubles:
- instance_doubles.feature
- class_doubles.feature
- object_doubles.feature
- dynamic_classes.feature
- partial_doubles.feature
+- configuring_responses:
+ - returning_a_value.feature
+ - raising_an_error.feature
+ - throwing.feature
+ - yielding.feature
+ - calling_the_original_implementation.feature
+ - block_implementation.feature
+- setting_constraints:
+ - matching_arguments.feature
+ - receive_counts.feature
+ - message_order.feature
+- mutating_constants:
+ - stub_defined_constant.feature
+ - stub_undefined_constant.feature
+ - hide_defined_constant.feature
+ - hide_undefined_constant.feature
+- working_with_legacy_code:
+ - any_instance.feature
+ - message_chains.feature
+- old_syntax:
+ - stub.feature
+ - should_receive.feature
+ - any_instance.feature
+ - stub_chain.feature
+ - unstub.feature
- outside_rspec:
- - configuration.feature
+ - minitest.feature
- standalone.feature
+- Changelog
View
93 features/README.md
@@ -1,75 +1,76 @@
-rspec-mocks helps to control the context in a code example by letting you set
-known return values, fake implementations of methods, and even expectations
-that specific messages are received by an object.
+rspec-mocks helps to control the context in a code example by letting you set known return
+values, fake implementations of methods, and even set expectations that specific messages
+are received by an object.
-You can do these three things on test doubles that rspec-mocks creates for you
-on the fly, or you can do them on objects that are part of your system.
+You can do these three things on test doubles that rspec-mocks creates for you on the fly, or
+you can do them on objects that are part of your system.
## Messages and Methods
-_Message_ and _method_ are metaphors that we use somewhat interchangeably, but
-they are subtly different. In Object Oriented Programming, objects communicate
-by sending _messages_ to one another. When an object receives a message, it
-invokes a _method_ with the same name as the message.
+_Message_ and _method_ are metaphors that we use somewhat interchangeably, but they are
+subtly different. In Object Oriented Programming, objects communicate by sending
+_messages_ to one another. When an object receives a message, it invokes a _method_ with the
+same name as the message.
## Test Doubles
-A test double is an object that stands in for another object in your system
-during a code example. Use the `double` method, passing in an optional identifier, to create one:
+A test double is an object that stands in for another object in your system during a code
+example. Use the `double` method, passing in an optional identifier, to create one:
- book = double("book")
+```ruby
+book = double("book")
+```
-Most of the time you will want some confidence that your doubles resemble an
-existing object in your system. Verifying doubles are provided for this
-purpose. If the existing object is available, they will prevent you from adding
-stubs and expectations for methods that do not exist or that have an invalid
-number of parameters.
+Most of the time you will want some confidence that your doubles resemble an existing
+object in your system. Verifying doubles are provided for this purpose. If the existing object
+is available, they will prevent you from adding stubs and expectations for methods that do
+not exist or that have invalid arguments.
- book = instance_double("Book", :pages => 250)
+```ruby
+book = instance_double("Book", :pages => 250)
+```
-Verifying doubles have some clever tricks to enable you to both test in
-isolation without your dependencies loaded while still being able to validate
-them against real objects.
+[Verifying doubles](./docs/verifying-doubles) have some clever tricks to enable you to both test in isolation without your
+dependencies loaded while still being able to validate them against real objects.
## Method Stubs
A method stub is an instruction to an object (real or test double) to return a
known value in response to a message:
- allow(die).to receive(:roll) { 3 }
+```ruby
+allow(die).to receive(:roll) { 3 }
+```
-This tells the `die` object to return the value `3` when it receives the `roll`
-message.
+This tells the `die` object to return the value `3` when it receives the `roll` message.
## Message Expectations
-A message expectation is an expectation that an object should receive a
-specific message during the course of a code example:
+A message expectation is an expectation that an object should receive a specific message
+during the course of a code example:
- describe Account do
- context "when closed" do
- it "logs an 'account closed' message" do
- logger = double()
- account = Account.new
- account.logger = logger
+```ruby
+describe Account do
+ context "when closed" do
+ it "logs an 'account closed' message" do
+ logger = double()
+ account = Account.new
+ account.logger = logger
- expect(logger).to receive(:account_closed).with(account)
+ expect(logger).to receive(:account_closed).with(account)
- account.close
- end
- end
+ account.close
end
+ end
+end
+```
-This example specifies that the `account` object sends the `logger` the
-`account_closed` message (with itself as an argument) when it receives the
-`close` message.
+This example specifies that the `account` object sends the `logger` the `account_closed`
+message (with itself as an argument) when it receives the `close` message.
## Issues
-The documentation for rspec-mocks is a work in progress. We'll be adding
-Cucumber features over time, and clarifying existing ones. If you have
-specific features you'd like to see added, find the existing documentation
-incomplete or confusing, or, better yet, wish to write a missing Cucumber
-feature yourself, please [submit an
-issue](http://github.com/rspec/rspec-mocks/issues) or a [pull
-request](http://github.com/rspec/rspec-mocks).
+The documentation for rspec-mocks is a work in progress. We'll be adding Cucumber
+features over time, and clarifying existing ones. If you have specific features you'd like to see
+added, find the existing documentation incomplete or confusing, or, better yet, wish to write
+a missing Cucumber feature yourself, please [submit an issue](http://github.com/rspec/rspec-mocks/issues) or a [pull request](http://github.com/rspec/rspec-mocks).
View
17 features/Scope.md
@@ -1,17 +0,0 @@
-Doubles, stubs, and message expectations are all cleaned out after each
-example. This ensures that each example can be run in isolation, and in any
-order.
-
-### `before(:example)`
-
-It is perfectly fine to set up doubles, stubs, and message expectations in
-a `before(:example)` hook, as that hook is executed in the scope of the example:
-
- before(:example) do
- @account = double('account')
- end
-
-### Do not create doubles, stubs, or message expectations in `before(:context)`
-
-If you do, they'll get cleaned out after the first example, and you will be
-very confused as to what's going on in the second example.
View
27 features/argument_matchers/README.md
@@ -1,27 +0,0 @@
-### Introduction
-
-Argument matchers can be used:
-
-* In stubs to constrain the scope of the stubbed method
-
- allow(obj).to receive(:foo).with(:bar) do |arg|
- #do something for :bar
- end
- allow(obj).to receive(:foo).with(:baz) do |arg|
- #do something for :baz
- end
-
-* In expectations to validate the arguments that should be received in a method call
-
- #create a double
- obj = double()
-
- #expect a message with given args
- expect(obj).to receive(:message).with('an argument')
-
-If more control is needed, one can use a block
-
- expect(obj).to receive(:message) do |arg1, arg2|
- # set expectations about the args in this block
- # and optionally set a return value
- end
View
31 features/argument_matchers/explicit.feature
@@ -1,31 +0,0 @@
-Feature: explicit arguments
-
- Allows you to explicitly specify the argument values
-
- Scenario: explicit arguments
- Given a file named "stub_explicit_args_spec.rb" with:
- """ruby
- describe "stubbed explicit arguments" do
- it "works on stubs" do
- object = Object.new
- allow(object).to receive(:foo).with(:this) do |arg|
- "got this"
- end
- allow(object).to receive(:foo).with(:that) do |arg|
- "got that"
- end
-
- expect(object.foo(:this)).to eq("got this")
- expect(object.foo(:that)).to eq("got that")
- end
-
- it "works on doubles and expectations" do
- object = double('foo')
- expect(object).to receive(:bar).with(:foo)
-
- object.bar(:foo)
- end
- end
- """
- When I run `rspec stub_explicit_args_spec.rb`
- Then the output should contain "2 examples, 0 failures"
View
85 features/argument_matchers/general_matchers.feature
@@ -1,85 +0,0 @@
-Feature: General matchers
-
- The `anything`, `any_args`, and `no_args` matchers can be used to require the method
- to have arguments (or not) without constraining the details of the argument, such as its
- type, pattern or value. The `anything` matcher only reflects a single argument, while
- the `any_args` matcher matches any arity.
-
- Scenario: anything argument matcher
- Given a file named "stub_anything_args_spec.rb" with:
- """ruby
- describe "stubbed anything() args spec" do
- it "works" do
- object = Object.new
- allow(object).to receive(:foo).with(anything) do
- "anything"
- end
-
- expect(object.foo(1)).to eq("anything")
- expect(object.foo(:that)).to eq("anything")
- end
- end
- """
- When I run `rspec stub_anything_args_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: any_args argument matcher
- Given a file named "stub_any_args_spec.rb" with:
- """ruby
- describe "stubbed any_args() args spec" do
- it "works" do
- object = Object.new
- allow(object).to receive(:foo).with(any_args) do
- "anything"
- end
-
- expect(object.foo(1)).to eq("anything")
- expect(object.foo(:that)).to eq("anything")
- expect(object.foo).to eq("anything")
- end
- end
- """
- When I run `rspec stub_any_args_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: no_args argument matcher
- Given a file named "stub_no_args_spec.rb" with:
- """ruby
- describe "stubbed no_args() args spec" do
- it "works for no args" do
- object = Object.new
- allow(object).to receive(:foo).with(no_args) do
- "nothing"
- end
- allow(object).to receive(:foo).with(anything) do
- "something"
- end
-
- expect(object.foo(:that)).to eq("something")
- expect(object.foo).to eq("nothing")
- end
- end
- """
- When I run `rspec stub_no_args_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: no_args argument matcher for expectations
- Given a file named "stub_no_args_expectations_spec.rb" with:
- """ruby
- describe "stubbed no_args() args spec for expectations" do
- it "works for no args" do
- object = Object.new
- expect(object).to receive(:foo).with(no_args)
-
- object.foo
- end
- it "fails for args" do
- object = Object.new
- expect(object).to receive(:foo).with(no_args)
-
- object.foo(:bar)
- end
- end
- """
- When I run `rspec stub_no_args_expectations_spec.rb`
- Then the output should contain "2 examples, 1 failure"
View
26 features/argument_matchers/type_matchers.feature
@@ -1,26 +0,0 @@
-Feature: stub with argument constraints
-
- You can further specify the behavior by constraining the type,
- format and/or number of arguments with the `#with()` method
- chained off of `#stub()`
-
- Scenario: an_instance_of argument matcher
- Given a file named "stub_an_instance_of_args_spec.rb" with:
- """ruby
- describe "stubbed an_instance_of() args spec" do
- it "works" do
- object = Object.new
- allow(object).to receive(:foo).with(an_instance_of(Symbol)) do
- "symbol"
- end
- allow(object).to receive(:foo).with(an_instance_of(String)) do
- "string"
- end
-
- expect(object.foo("bar")).to eq("string")
- expect(object.foo(:that)).to eq("symbol")
- end
- end
- """
- When I run `rspec stub_an_instance_of_args_spec.rb`
- Then the output should contain "1 example, 0 failures"
View
35 features/basics/allowing_messages.feature
@@ -0,0 +1,35 @@
+Feature: Allowing messages
+
+ [Test doubles](./test-doubles) are "strict" by default -- messages that have not been specifically
+ allowed or expected will trigger an error. Use `allow(...).to receive(...)` to configure
+ which messages are the double is allowed to receive. You can also use `allow(...).to
+ receive_messages(...)` to configure allowed messages (and return values) in bulk.
+
+ Scenario: Allowed messages return nil by default
+ Given a file named "allow_message_spec.rb" with:
+ """ruby
+ RSpec.describe "allow" do
+ it "returns nil from allowed messages" do
+ dbl = double("Some Collaborator")
+ allow(dbl).to receive(:foo)
+ expect(dbl.foo).to be_nil
+ end
+ end
+ """
+ When I run `rspec allow_message_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Messages can be allowed in bulk using `receive_messages`
+ Given a file named "receive_messages_spec.rb" with:
+ """ruby
+ RSpec.describe "receive_messages" do
+ it "configures return values for the provided messages" do
+ dbl = double("Some Collaborator")
+ allow(dbl).to receive_messages(:foo => 2, :bar => 3)
+ expect(dbl.foo).to eq(2)
+ expect(dbl.bar).to eq(3)
+ end
+ end
+ """
+ When I run `rspec receive_messages_spec.rb`
+ Then the examples should all pass
View
73 features/basics/expecting_messages.feature
@@ -0,0 +1,73 @@
+Feature: Expecting messages
+
+ Use `expect(...).to receive(...)` to expect a message on a [test double](./test-doubles). Unfulfilled
+ message expectations trigger failures when the example completes. You can also use
+ `expect(...).not_to receive(...)` to set a negative message expectation.
+
+ Scenario: Failing positive message expectation
+ Given a file named "unfulfilled_message_expectation_spec.rb" with:
+ """ruby
+ RSpec.describe "An unfulfilled positive message expectation" do
+ it "triggers a failure" do
+ dbl = double("Some Collaborator")
+ expect(dbl).to receive(:foo)
+ end
+ end
+ """
+ When I run `rspec unfulfilled_message_expectation_spec.rb`
+ Then it should fail with:
+ """
+ 1) An unfulfilled positive message expectation triggers a failure
+ Failure/Error: expect(dbl).to receive(:foo)
+ (Double "Some Collaborator").foo(any args)
+ expected: 1 time with any arguments
+ received: 0 times with any arguments
+ """
+
+ Scenario: Passing positive message expectation
+ Given a file named "fulfilled_message_expectation_spec.rb" with:
+ """ruby
+ RSpec.describe "A fulfilled positive message expectation" do
+ it "passes" do
+ dbl = double("Some Collaborator")
+ expect(dbl).to receive(:foo)
+ dbl.foo
+ end
+ end
+ """
+ When I run `rspec fulfilled_message_expectation_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Failing negative message expectation
+ Given a file named "negative_message_expectation_spec.rb" with:
+ """ruby
+ RSpec.describe "A negative message expectation" do
+ it "fails when the message is received" do
+ dbl = double("Some Collaborator").as_null_object
+ expect(dbl).not_to receive(:foo)
+ dbl.foo
+ end
+ end
+ """
+ When I run `rspec negative_message_expectation_spec.rb`
+ Then it should fail with:
+ """
+ 1) A negative message expectation fails when the message is received
+ Failure/Error: dbl.foo
+ (Double "Some Collaborator").foo(no args)
+ expected: 0 times with any arguments
+ received: 1 time
+ """
+
+ Scenario: Passing negative message expectation
+ Given a file named "negative_message_expectation_spec.rb" with:
+ """ruby
+ RSpec.describe "A negative message expectation" do
+ it "passes if the message is never received" do
+ dbl = double("Some Collaborator").as_null_object
+ expect(dbl).not_to receive(:foo)
+ end
+ end
+ """
+ When I run `rspec negative_message_expectation_spec.rb`
+ Then the examples should all pass
View
35 features/basics/null_object_doubles.feature
@@ -0,0 +1,35 @@
+Feature: Null object doubles
+
+ [Test doubles](./test-doubles) are strict by default, raising errors when they receive messages that have not
+ been allowed or expected. You can chain `as_null_object` off of `double` in order to make
+ the double "loose". For any message that has not explicitly allowed or expected, the double
+ will return itself. It acts as a block-hole null object, allowing arbitrarily deep method chains.
+
+ Scenario: `as_null_object` allows arbitrarily deep message chains and returns itself
+ Given a file named "as_null_object_spec.rb" with:
+ """ruby
+ RSpec.describe "as_null_object" do
+ it "returns itself" do
+ dbl = double("Some Collaborator").as_null_object
+ expect(dbl.foo.bar.bazz).to be(dbl)
+ end
+ end
+ """
+ When I run `rspec as_null_object_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Individual methods can still be allowed or expected
+ Given a file named "as_null_object_spec.rb" with:
+ """ruby
+ RSpec.describe "as_null_object" do
+ it "can allow individual methods" do
+ dbl = double("Some Collaborator", :foo => 3).as_null_object
+ allow(dbl).to receive(:bar).and_return(4)
+
+ expect(dbl.foo).to eq(3)
+ expect(dbl.bar).to eq(4)
+ end
+ end
+ """
+ When I run `rspec as_null_object_spec.rb`
+ Then the examples should all pass
View
70 features/basics/partial_test_doubles.feature
@@ -0,0 +1,70 @@
+Feature: Partial test doubles
+
+ A _partial test double_ is an extension of a real object in a system that is instrumented with
+ test-double like behaviour in the context of a test. This technique is very common in Ruby
+ because we often see class objects acting as global namespaces for methods. For example,
+ in Rails:
+
+ ```ruby
+ person = double("person")
+ allow(Person).to receive(:find) { person }
+ ```
+
+ In this case we're instrumenting Person to return the person object we've defined whenever
+ it receives the `find` message. We can also set a message expectation so that the example
+ fails if `find` is not called:
+
+ ```ruby
+ person = double("person")
+ expect(Person).to receive(:find) { person }
+ ```
+
+ RSpec replaces the method we're stubbing or mocking with its own test-double-like method.
+ At the end of the example, RSpec verifies any message expectations, and then restores the
+ original methods.
+
+ Note: we recommend enabling the [`verify_partial_doubles`](../verifying-doubles/partial-doubles) config option.
+
+ Scenario: Only the specified methods are redefined
+ Given a file named "partial_double_spec.rb" with:
+ """ruby
+ RSpec.describe "A partial double" do
+ # Note: stubbing a string like this is a terrible idea.
+ # This is just for demonstration purposes.
+ let(:string) { "a string" }
+ before { allow(string).to receive(:length).and_return(500) }
+
+ it "redefines the specified methods" do
+ expect(string.length).to eq(500)
+ end
+
+ it "does not effect other methods" do
+ expect(string.reverse).to eq("gnirts a")
+ end
+ end
+ """
+ When I run `rspec partial_double_spec.rb`
+ Then the examples should all pass
+
+ Scenario: The original method is restored when the example completes
+ Given a file named "partial_double_spec.rb" with:
+ """ruby
+ class User
+ def self.find(id)
+ :original_return_value
+ end
+ end
+
+ RSpec.describe "A partial double" do
+ it "redefines a method" do
+ allow(User).to receive(:find).and_return(:redefined)
+ expect(User.find(3)).to eq(:redefined)
+ end
+
+ it "restores the redefined method after the example completes" do
+ expect(User.find(3)).to eq(:original_return_value)
+ end
+ end
+ """
+ When I run `rspec partial_double_spec.rb --order defined`
+ Then the examples should all pass
View
99 features/basics/scope.feature
@@ -0,0 +1,99 @@
+Feature: Scope
+
+ All rspec-mocks constructs have a per-example lifecycle. Message expectations are verified
+ after each example. Doubles, method stubs, stubbed constants, etc. are all cleaned up after
+ each example. This ensures that each example can be run in isolation, and in any order.
+
+ It is perfectly fine to set up doubles, stubs, and message expectations in a
+ `before(:example)` hook, as that hook is executed in the scope of the example:
+
+ ```ruby
+ before(:example) do
+ allow(MyClass).to receive(:foo)
+ end
+ ```
+
+ Since `before(:context)` runs outside the scope of any individual example, usage of
+ rspec-mocks features is not supported there. You can, however, create a temporary scope in
+ _any_ arbitrary context, including in a `before(:context)` hook, using
+ `RSpec::Mocks.with_temporary_scope { }`.
+
+ Scenario: Cannot create doubles in a `before(:context)` hook
+ Given a file named "before_context_spec.rb" with:
+ """ruby
+ RSpec.describe "Creating a double in a before(:context) hook" do
+ before(:context) do
+ @dbl = double(:foo => 13)
+ end
+
+ it "fails before it gets to the examples" do
+ expect(@dbl.foo).to eq(13)
+ end
+ end
+ """
+ When I run `rspec before_context_spec.rb`
+ Then it should fail with:
+ """
+ The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported.
+ """
+
+ Scenario: Use `with_temporary_scope` to create and use a double in a `before(:context)` hook
+ Given a file named "with_temporary_scope_spec.rb" with:
+ """ruby
+ RSpec.describe "Creating a double in a before(:context) hook" do
+ before(:context) do
+ RSpec::Mocks.with_temporary_scope do
+ dbl = double(:foo => 13)
+ @result = dbl.foo
+ end
+ end
+
+ it "allows a double to be created and used from within a with_temporary_scope block" do
+ expect(@result).to eq(13)
+ end
+ end
+ """
+ When I run `rspec with_temporary_scope_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Doubles cannot be reused in another example
+ Given a file named "leak_test_double_spec.rb" with:
+ """ruby
+ class Account
+ class << self
+ attr_accessor :logger
+ end
+
+ def initialize
+ @balance = 0
+ end
+
+ attr_reader :balance
+
+ def credit(amount)
+ @balance += amount
+ self.class.logger.log("Credited $#{amount}")
+ end
+ end
+
+ RSpec.describe Account do
+ it "logs each credit" do
+ Account.logger = logger = double("Logger")
+ expect(logger).to receive(:log).with("Credited $15")
+ account = Account.new
+ account.credit(15)
+ end
+
+ it "keeps track of the balance" do
+ account = Account.new
+ expect { account.credit(10) }.to change { account.balance }.by(10)
+ end
+ end
+ """
+ When I run `rspec leak_test_double_spec.rb`
+ Then it should fail with the following output:
+ | 2 examples, 1 failure |
+ | |
+ | 1) Account keeps track of the balance |
+ | Failure/Error: self.class.logger.log("Credited $#{amount}") |
+ | Double "Logger" was originally created in one example but has leaked into another example and can no longer be used. |
View
144 features/basics/spies.feature
@@ -0,0 +1,144 @@
+Feature: Spies
+
+ [Message expectations](./expecting-messages) put an example's expectation at the start, before you've invoked the
+ code-under-test. Many developers prefer using an act-arrange-assert (or given-when-then)
+ pattern for structuring tests. Spies are an alternate type of test double that support this
+ pattern by allowing you to expect that a message has been received after the fact, using
+ `have_received`.
+
+ You can use any test double (or partial double) as a spy, but the double must be setup to
+ spy on the messages you care about. [Null object doubles](./null-object-doubles) automatically spy on all messages,
+ or you can [allow a message](./allowing-messages) to spy on it.
+
+ `have_received` supports the same fluent interface for [setting constraints](../setting-constraints) that normal message expectations do.
+
+ Note: The `have_received` API shown here will only work if you are using rspec-expectations.
+
+ Scenario: Use a null object double as a spy
+ Given a file named "null_object_spy_spec.rb" with:
+ """ruby
+ RSpec.describe "have_received" do
+ it "passes when the message has been received" do
+ invitation = double('invitation').as_null_object
+ invitation.deliver
+ expect(invitation).to have_received(:deliver)
+ end
+ end
+ """
+ When I run `rspec null_object_spy_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Spy on a method on a partial double
+ Given a file named "partial_double_spy_spec.rb" with:
+ """ruby
+ class Invitation
+ def self.deliver; end
+ end
+
+ RSpec.describe "have_received" do
+ it "passes when the expectation is met" do
+ allow(Invitation).to receive(:deliver)
+ Invitation.deliver
+ expect(Invitation).to have_received(:deliver)
+ end
+ end
+ """
+ When I run `rspec partial_double_spy_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Failure when the message has not been received
+ Given a file named "failure_spec.rb" with:
+ """ruby
+ class Invitation
+ def self.deliver; end
+ end
+
+ RSpec.describe "failure when the message has not been received" do
+ example "for a null object double" do
+ invitation = double('invitation').as_null_object
+ expect(invitation).to have_received(:deliver)
+ end
+
+ example "for a partial double" do
+ allow(Invitation).to receive(:deliver)
+ expect(Invitation).to have_received(:deliver)
+ end
+ end
+ """
+ When I run `rspec failure_spec.rb --order defined`
+ Then it should fail with:
+ """
+ 1) failure when the message has not been received for a null object double
+ Failure/Error: expect(invitation).to have_received(:deliver)
+ (Double "invitation").deliver(any args)
+ expected: 1 time with any arguments
+ received: 0 times with any arguments
+ """
+ And it should fail with:
+ """
+ 2) failure when the message has not been received for a partial double
+ Failure/Error: expect(Invitation).to have_received(:deliver)
+ (<Invitation (class)>).deliver(any args)
+ expected: 1 time with any arguments
+ received: 0 times with any arguments
+ """
+
+ Scenario: Set constraints using the fluent interface
+ Given a file named "setting_constraints_spec.rb" with:
+ """ruby
+ RSpec.describe "An invitiation" do
+ let(:invitation) { double("invitation").as_null_object }
+
+ before do
+ invitation.deliver("foo@example.com")
+ invitation.deliver("bar@example.com")
+ end
+
+ it "passes when a count constraint is satisfied" do
+ expect(invitation).to have_received(:deliver).twice
+ end
+
+ it "passes when an order constraint is satisifed" do
+ expect(invitation).to have_received(:deliver).with("foo@example.com").ordered
+ expect(invitation).to have_received(:deliver).with("bar@example.com").ordered
+ end
+
+ it "fails when a count constraint is not satisfied" do
+ expect(invitation).to have_received(:deliver).at_least(3).times
+ end
+
+ it "fails when an order constraint is not satisifed" do
+ expect(invitation).to have_received(:deliver).with("bar@example.com").ordered
+ expect(invitation).to have_received(:deliver).with("foo@example.com").ordered
+ end
+ end
+ """
+ When I run `rspec setting_constraints_spec.rb --order defined`
+ Then it should fail with the following output:
+ | 4 examples, 2 failures |
+ | |
+ | 1) An invitiation fails when a count constraint is not satisfied |
+ | Failure/Error: expect(invitation).to have_received(:deliver).at_least(3).times |
+ | (Double "invitation").deliver(any args) |
+ | expected: at least 3 times with any arguments |
+ | received: 2 times with any arguments |
+ | |
+ | 2) An invitiation fails when an order constraint is not satisifed |
+ | Failure/Error: expect(invitation).to have_received(:deliver).with("foo@example.com").ordered |
+ | Double "invitation" received :deliver out of order |
+
+ Scenario: `have_received` generates a good example description
+ Given a file named "generates_description_spec.rb" with:
+ """ruby
+ RSpec.describe "An invitation" do
+ subject(:invitation) { double('invitation').as_null_object }
+ before { invitation.deliver }
+ it { is_expected.to have_received(:deliver) }
+ end
+ """
+ When I run `rspec --format documentation generates_description_spec.rb`
+ Then it should pass with:
+ """
+ An invitation
+ should have received deliver(any args) 1 time
+ """
View
41 features/basics/test_doubles.feature
@@ -0,0 +1,41 @@
+Feature: Test Doubles
+
+ _Test double_ is a generic term for any object that stands in for a real object during a test
+ (think "stunt double"). You create one using the `double` method. Doubles are "strict" by
+ default -- any message you have not allowed or expected will trigger an error -- but you can
+ [switch a double to being "loose"](./null-object-doubles). When creating a double, you can allow messages (and set
+ their return values) by passing a hash.
+
+ Once you have a test double, you can [allow](./allowing-messages) or [expect](./expecting-messages) messages on it.
+
+ We recommend you use [verifying doubles](../verifying-doubles) whenever possible.
+
+ Scenario: Doubles are strict by default
+ Given a file named "double_spec.rb" with:
+ """ruby
+ RSpec.describe "double" do
+ it "raises errors when messages not allowed or expected are received" do
+ dbl = double("Some Collaborator")
+ dbl.foo
+ end
+ end
+ """
+ When I run `rspec double_spec.rb`
+ Then it should fail with:
+ """
+ Double "Some Collaborator" received unexpected message :foo with (no args)
+ """
+
+ Scenario: A hash can be used to define allowed messages and return values
+ Given a file named "double_spec.rb" with:
+ """ruby
+ RSpec.describe "double" do
+ it "raises errors when messages not allowed or expected are received" do
+ dbl = double("Some Collaborator", :foo => 3, :bar => 4)
+ expect(dbl.foo).to eq(3)
+ expect(dbl.bar).to eq(4)
+ end
+ end
+ """
+ When I run `rspec double_spec.rb`
+ Then the examples should all pass
View
12 features/configuring_responses/README.md
@@ -0,0 +1,12 @@
+When [allowing](../basics/allowing-messages) or [expecting](../basics/expecting-messages) messages, the default response is to return `nil`. Several
+methods are provided to configure how the test double responds to the message.
+
+* <a href="./configuring-responses/returning-a-value">`and_return`</a>
+* <a href="./configuring-responses/raising-an-error">`and_raise`</a>
+* <a href="./configuring-responses/throwing">`and_throw`</a>
+* <a href="./configuring-responses/yielding">`and_yield`</a>
+* <a href="./configuring-responses/calling-the-original-implementation">`and_call_original`</a>
+
+In addition, you can provide a [block implementation](./block-implementation) to respond in any manner you wish.
+
+Note: for simplicity, the examples here use `allow` rather than `expect`, but these APIs apply equally to both cases.
View
131 features/configuring_responses/block_implementation.feature
@@ -0,0 +1,131 @@
+Feature: Block implementation
+
+ When you pass a block, RSpec will use your block as the implementation of the method. Any
+ arguments (or a block) provided by the caller will be yielded to your block implementation.
+ This feature is extremely flexible, and supports many use cases that are not directly
+ supported by the more declaritive fluent interface.
+
+ You can pass a block to any of the fluent interface methods:
+
+ * `allow(dbl).to receive(:foo) { do_something }`
+ * `allow(dbl).to receive(:foo).with("args") { do_something }`
+ * `allow(dbl).to receive(:foo).once { do_something }`
+ * `allow(dbl).to receive(:foo).ordered { do_something }`
+
+ Some of the more common use cases for block implementations are shown below, but this
+ is not an exhaustive list.
+
+ Scenario: Use a block to specify a return value with a terser syntax
+ Given a file named "return_value_spec.rb" with:
+ """ruby
+ RSpec.describe "Specifying a return value using a block" do
+ it "returns the block's return value" do
+ dbl = double
+ allow(dbl).to receive(:foo) { 14 }
+ expect(dbl.foo).to eq(14)
+ end
+ end
+ """
+ When I run `rspec return_value_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Use a block to verify arguments
+ Given a file named "verify_arguments_spec.rb" with:
+ """ruby
+ RSpec.describe "Verifying arguments using a block" do
+ it "fails when the arguments do not meet the expectations set in the block" do
+ dbl = double
+
+ allow(dbl).to receive(:foo) do |arg|
+ expect(arg).to eq("bar")
+ end
+
+ dbl.foo(nil)
+ end
+ end
+ """
+ When I run `rspec verify_arguments_spec.rb`
+ Then it should fail with:
+ """
+ Failure/Error: expect(arg).to eq("bar")
+ """
+
+ Scenario: Use a block to perform a calculation
+ Given a file named "perform_calculation_spec.rb" with:
+ """ruby
+ RSpec.describe "Performing a calculation using a block" do
+ it "returns the block's return value" do
+ loan = double("Loan", :amount => 100)
+
+ allow(loan).to receive(:required_payment_for_rate) do |rate|
+ loan.amount * rate
+ end
+
+ expect(loan.required_payment_for_rate(0.05)).to eq(5)
+ expect(loan.required_payment_for_rate(0.1)).to eq(10)
+ end
+ end
+ """
+ When I run `rspec perform_calculation_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Yield to the caller's block
+ Given a file named "yield_to_caller_spec.rb" with:
+ """ruby
+ RSpec.describe "When the caller passes a block" do
+ it "can be yielded to from your implementation block" do
+ dbl = double
+ allow(dbl).to receive(:foo) { |&block| block.call(14) }
+ expect { |probe| dbl.foo(&probe) }.to yield_with_args(14)
+ end
+ end
+ """
+ When I run `rspec yield_to_caller_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Delegate to partial double's original implementation within the block
+ Given a file named "delegate_to_original_spec.rb" with:
+ """ruby
+ class Calculator
+ def self.add(x, y)
+ x + y
+ end
+ end
+
+ RSpec.describe "When using a block implementation on a partial double" do
+ it "supports delegating to the original implementation" do
+ original_add = Calculator.method(:add)
+
+ allow(Calculator).to receive(:add) do |x, y|
+ original_add.call(x, y) * 2
+ end
+
+ expect(Calculator.add(2, 5)).to eq(14)
+ end
+ end
+ """
+ When I run `rspec delegate_to_original_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Simulating a transient network failure
+ Given a file named "simulate_transient_network_failure_spec.rb" with:
+ """ruby
+ RSpec.describe "An HTTP API client" do
+ it "can simulate transient network failures" do
+ client = double("MyHTTPClient")
+
+ call_count = 0
+ allow(client).to receive(:fetch_data) do
+ call_count += 1
+ call_count.odd? ? raise("timeout") : { :count => 15 }
+ end
+
+ expect { client.fetch_data }.to raise_error("timeout")
+ expect(client.fetch_data).to eq(:count => 15)
+ expect { client.fetch_data }.to raise_error("timeout")
+ expect(client.fetch_data).to eq(:count => 15)
+ end
+ end
+ """
+ When I run `rspec simulate_transient_network_failure_spec.rb`
+ Then the examples should all pass
View
52 features/configuring_responses/calling_the_original_implementation.feature
@@ -0,0 +1,52 @@
+Feature: Calling the original implementation
+
+ Use `and_call_original` to make a partial double response as it normally would. This can
+ be useful when you want to expect a message without interfering with how it responds. You
+ can also use it to configure the default response for most arguments, and then override
+ that for specific arguments using `with`.
+
+ Note: `and_call_original` is only supported on partial doubles, as normal test doubles do
+ not have an original implementation.
+
+ Background:
+ Given a file named "lib/calculator.rb" with:
+ """ruby
+ class Calculator
+ def self.add(x, y)
+ x + y
+ end
+ end
+ """
+
+ Scenario: `and_call_original` makes the partial double respond as it normally would
+ Given a file named "spec/and_call_original_spec.rb" with:
+ """ruby
+ require 'calculator'
+
+ RSpec.describe "and_call_original" do
+ it "responds as it normally would" do
+ expect(Calculator).to receive(:add).and_call_original
+ expect(Calculator.add(2, 3)).to eq(5)
+ end
+ end
+ """
+ When I run `rspec spec/and_call_original_spec.rb`
+ Then the examples should all pass
+
+ Scenario: `and_call_original` can configure a default response that can be overriden for specific args
+ Given a file named "spec/and_call_original_spec.rb" with:
+ """ruby
+ require 'calculator'
+
+ RSpec.describe "and_call_original" do
+ it "can be overriden for specific arguments using #with" do
+ allow(Calculator).to receive(:add).and_call_original
+ allow(Calculator).to receive(:add).with(2, 3).and_return(-5)
+
+ expect(Calculator.add(2, 2)).to eq(4)
+ expect(Calculator.add(2, 3)).to eq(-5)
+ end
+ end
+ """
+ When I run `rspec spec/and_call_original_spec.rb`
+ Then the examples should all pass
View
28 features/configuring_responses/raising_an_error.feature
@@ -0,0 +1,28 @@
+Feature: Raising an error
+
+ Use `and_raise` to make the test double raise an error when it receives the message. Any of the following forms are supported:
+
+ * `and_raise(ExceptionClass)`
+ * `and_raise("message")`
+ * `and_raise(ExceptionClass, "message")`
+ * `and_raise(instance_of_an_exception_class)`
+
+ Scenario: Raising an error
+ Given a file named "raises_an_error_spec.rb" with:
+ """ruby
+ RSpec.describe "Making it raise an error" do
+ it "raises the provided exception" do
+ dbl = double
+ allow(dbl).to receive(:foo).and_raise("boom")
+ dbl.foo
+ end
+ end
+ """
+ When I run `rspec raises_an_error_spec.rb`
+ Then it should fail with:
+ """
+ 1) Making it raise an error raises the provided exception
+ Failure/Error: dbl.foo
+ RuntimeError:
+ boom
+ """
View
52 features/configuring_responses/returning_a_value.feature
@@ -0,0 +1,52 @@
+Feature: Returning a value
+
+ Use `and_return` to specify a return value. Pass `and_return` multiple values to specify
+ different return values for consecutive calls. The final value will continue to be returned if
+ the message is received additional times.
+
+ Scenario: Nil is returned by default
+ Given a file named "returns_nil_spec.rb" with:
+ """ruby
+ RSpec.describe "The default response" do
+ it "returns nil when no response has been configured" do
+ dbl = double
+ allow(dbl).to receive(:foo)
+ expect(dbl.foo).to be_nil
+ end
+ end
+ """
+ When I run `rspec returns_nil_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Specify a return value
+ Given a file named "and_return_spec.rb" with:
+ """ruby
+ RSpec.describe "Specifying a return value" do
+ it "returns the specified return value" do
+ dbl = double
+ allow(dbl).to receive(:foo).and_return(14)
+ expect(dbl.foo).to eq(14)
+ end
+ end
+ """
+ When I run `rspec and_return_spec.rb`
+ Then the examples should all pass
+
+ Scenario: Specify different return values for multiple calls
+ Given a file named "multiple_calls_spec.rb" with:
+ """ruby
+ RSpec.describe "When the method is called multiple times" do
+ it "returns the specified values in order, then keeps returning the last value" do
+ dbl = double
+ allow(dbl).to receive(:foo).and_return(1, 2, 3)
+
+ expect(dbl.foo).to eq(1)
+ expect(dbl.foo).to eq(2)
+ expect(dbl.foo).to eq(3)
+ expect(dbl.foo).to eq(3)
+ expect(dbl.foo).to eq(3)
+ end
+ end
+ """
+ When I run `rspec multiple_calls_spec.rb`
+ Then the examples should all pass
View
36 features/configuring_responses/throwing.feature
@@ -0,0 +1,36 @@
+Feature: Throwing
+
+ Use `and_throw` to make the test double throw the provided symbol, optionally with the provided argument.
+
+ * `and_throw(:symbol)`
+ * `and_throw(:symbol, argument)`
+
+ Scenario: Throw a symbol
+ Given a file named "and_throw_spec.rb" with:
+ """ruby
+ RSpec.describe "Making it throw a symbol" do
+ it "throws the provided symbol" do
+ dbl = double
+ allow(dbl).to receive(:foo).and_throw(:hello)
+
+ catch :hello do
+ dbl.foo
+ fail "should not get here"
+ end
+ end
+
+ it "includes the provided argument when throwing" do
+ dbl = double
+ allow(dbl).to receive(:foo).and_throw(:hello, "world")
+
+ arg = catch :hello do
+ dbl.foo
+ fail "should not get here"
+ end
+
+ expect(arg).to eq("world")
+ end
+ end
+ """
+ When I run `rspec and_throw_spec.rb`
+ Then the examples should all pass
View
76 features/configuring_responses/yielding.feature
@@ -0,0 +1,76 @@
+Feature: Yielding
+
+ Use `and_yield` to make the test double yield the provided arguments when it receives the
+ message. If the caller does not provide a block, or the caller's block does not accept the
+ provided arguments, an error will be raised. If you want to yield multiple times, chain
+ multiple `and_yield` calls together.
+
+ Scenario: Yield an argument
+ Given a file named "yield_arguments_spec.rb" with:
+ """ruby
+ RSpec.describe "Making it yield arguments" do
+ it "yields the provided args" do
+ dbl = double
+ allow(dbl).to receive(:foo).and_yield(2, 3)
+
+ x = y = nil
+ dbl.foo { |a, b| x, y = a, b }
+ expect(x).to eq(2)
+ expect(y).to eq(3)
+ end
+ end
+ """
+ When I run `rspec yield_arguments_spec.rb`
+ Then the examples should all pass
+
+ Scenario: It fails when the caller does not provide a block
+ Given a file named "no_caller_block_spec.rb" with:
+ """ruby
+ RSpec.describe "Making it yield" do
+ it "fails when the caller does not provide a block" do
+ dbl = double
+ allow(dbl).to receive(:foo).and_yield(2, 3)
+ dbl.foo
+ end
+ end
+ """
+ When I run `rspec no_caller_block_spec.rb`
+ Then it should fail with:
+ """
+ Double asked to yield |[2, 3]| but no block was passed
+ """
+
+ Scenario: It fails when the caller's block does not accept the provided arguments
+ Given a file named "arg_mismatch_spec.rb" with:
+ """ruby
+ RSpec.describe "Making it yield" do
+ it "fails when the caller's block does not accept the provided arguments" do
+ dbl = double
+ allow(dbl).to receive(:foo).and_yield(2, 3)
+ dbl.foo { |x| }
+ end
+ end
+ """
+ When I run `rspec arg_mismatch_spec.rb`
+ Then it should fail with:
+ """
+ Double yielded |2, 3| to block with arity of 1
+ """
+
+ Scenario: Yield multiple times
+ Given a file named "yield_multiple_times_spec.rb" with:
+ """
+ RSpec.describe "Making it yield multiple times" do
+ it "yields the specified args in succession" do
+ yielded = []
+
+ dbl = double
+ allow(dbl).to receive(:foo).and_yield(1).and_yield(2).and_yield(3)
+ dbl.foo { |x| yielded << x }
+
+ expect(yielded).to eq([1, 2, 3])
+ end
+ end
+ """
+ When I run `rspec yield_multiple_times_spec.rb`
+ Then the examples should all pass
View
75 features/message_expectations/README.md
@@ -1,75 +0,0 @@
-### Basics
-
- # create a double
- obj = double()
-
- # expect a message
- expect(obj).to receive(:message)
-
- # specify a return value
- expect(obj).to receive(:message) { :value }
- expect(obj).to receive(:message).and_return(:value)
-
- # specify multiple message/return value pairs
- expect(obj).to receive_messages(:message => :value, :another_message => :another_value)
-
-These forms are somewhat interchangeable. The difference is that the
-block contents are evaluated lazily when the `obj` receives the
-`message` message, whereas the others are evaluated as they are read.
-
-### Fake implementation
-
- expect(obj).to receive(:message) do |arg1, arg2|
- # set expectations about the args in this block
- # and set a return value
- end
-
-### Using the original implementation
-
- expect(obj).to receive(:message).and_call_original
-
-### Raising/Throwing
-
- expect(obj).to receive(:message).and_raise("this error")
- expect(obj).to receive(:message).and_throw(:this_symbol)
-
-You can also use the block format:
-
- expect(obj).to receive(:message) { raise "this error" }
- expect(obj).to receive(:message) { throw :this_symbol }
-
-### Argument constraints
-
-#### Explicit arguments
-
- expect(obj).to receive(:message).with('an argument')
- expect(obj).to receive(:message).with('more_than', 'one_argument')
-
-#### Argument matchers
-
- expect(obj).to receive(:message).with(anything())
- expect(obj).to receive(:message).with(an_instance_of(Money))
- expect(obj).to receive(:message).with(hash_including(:a => 'b'))
-
-#### Regular expressions
-
- expect(obj).to receive(:message).with(/abc/)
-
-### Counts
-
- expect(obj).to receive(:message).once
- expect(obj).to receive(:message).twice
- expect(obj).to receive(:message).exactly(3).times
-
- expect(obj).to receive(:message).at_least(:once)
- expect(obj).to receive(:message).at_least(:twice)
- expect(obj).to receive(:message).at_least(n).times
-
- expect(obj).to receive(:message).at_most(:once)
- expect(obj).to receive(:message).at_most(:twice)
- expect(obj).to receive(:message).at_most(n).times
-
-### Ordering
-
- expect(obj).to receive(:one).ordered
- expect(obj).to receive(:two).ordered
View
26 features/message_expectations/allow_any_instance_of.feature
@@ -1,26 +0,0 @@
-Feature: allow a message on any instance of a class
-
- Use `allow_any_instance_of(Class).to receive` when you want to configure how
- instances of the given class respond to a message without setting an
- expectation that the message will be received.
-
- Scenario: allowing a message on any instance of a class
- Given a file named "example_spec.rb" with:
- """ruby
- describe "any_instance.to receive" do
- before do
- allow_any_instance_of(Object).to receive(:foo).and_return(:return_value)
- end
-
- it "allows any instance of the class to receive the message" do
- o = Object.new
- expect(o.foo).to eq(:return_value)
- end
-
- it "passes even if no instances receive that message" do
- o = Object.new
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the examples should all pass
View
43 features/message_expectations/any_instance.feature
@@ -1,43 +0,0 @@
-Feature: expect a message on any instance of a class
-
- Use `expect_any_instance_of(Class).to receive` to set an expectation that one
- (and only one) instance of a class receives a message before the example is
- completed.
-
- The example will fail if no instance receives the specified message.
-
- Scenario: expect a message on any instance of a class
- Given a file named "example_spec.rb" with:
- """ruby
- describe "expect_any_instance_of" do
- before do
- expect_any_instance_of(Object).to receive(:foo).and_return(:return_value)
- end
-
- it "verifies that one instance of the class receives the message" do
- o = Object.new
- expect(o.foo).to eq(:return_value)
- end
-
- it "fails unless an instance receives that message" do
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the output should contain "2 examples, 1 failure"
- And the output should contain "1) expect_any_instance_of fails unless an instance receives that message"
-
- Scenario: expect a message on any instance of a class (should syntax)
- Given a file named "example_spec.rb" with:
- """ruby
- describe "any_instance.should_receive" do
- it "verifies that one instance of the class receives the message" do
- Object.any_instance.should_receive(:foo).and_return(:return_value)
-
- o = Object.new
- expect(o.foo).to eq(:return_value)
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the examples should all pass
View
55 features/message_expectations/block_local_expectations.feature.pending
@@ -1,55 +0,0 @@
-Feature: block local expectations
-
- Background:
- Given a file named "lib/account.rb" with:
- """
- class Account
- def self.create
- yield new
- end
-
- def opening_balance(amount, currency)
- end
- end
- """
-
- Scenario: passing example
- Given a file named "spec/account_spec.rb" with:
- """
- require 'account'
-
- describe "account DSL" do
- it "it succeeds when the block local receives the given call" do
- account = double("Account")
- Account.should_receive(:create).and_yield(account) do |account|
- account.should_receive(:opening_balance).with(100, :USD)
- end
- Account.create do |account|
- account.opening_balance 100, :USD
- end
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: failing example
-
- Given a file named "spec/account_spec.rb" with:
- """
- require 'account'
-
- describe "account DSL" do
- it "fails when the block local does not receive the expected call" do
- Account.should_receive(:create).and_yield do |account|
- account.should_receive(:opening_balance).with(100, :USD)
- end
- Account.create do |account|
- # opening_balance is not called here
- end
- end
- end
- """
-
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 1 failure"
View
23 features/message_expectations/call_original.feature
@@ -1,23 +0,0 @@
-Feature: Calling the original method
-
- You can use `and_call_original` on the fluent interface
- to "pass through" the received message to the original method.
-
- Scenario: expect a message
- Given a file named "call_original_spec.rb" with:
- """ruby
- class Addition
- def self.two_plus_two
- 4
- end
- end
-
- describe "and_call_original" do
- it "delegates the message to the original implementation" do
- expect(Addition).to receive(:two_plus_two).and_call_original
- expect(Addition.two_plus_two).to eq(4)
- end
- end
- """
- When I run `rspec call_original_spec.rb`
- Then the examples should all pass
View
107 features/message_expectations/expect_message_using_expect.feature
@@ -1,107 +0,0 @@
-Feature: expect message using `expect`
-
- Use `expect(object).to receive(:message)` to set an expectation that
- `object` should receive the message `:message` before the example is
- completed.
-
- Note: You can use `expect_any_instance_of` when you don't have a reference
- to the object that receives a message in your test. For more information,
- see the message_expectations/expect_any_instance_of feature.
-
- Scenario: expect a message
- Given a file named "spec/account_spec.rb" with:
- """ruby
- require "account"
-
- describe Account do
- context "when closed" do
- it "logs an account closed message" do
- logger = double("logger")
- account = Account.new logger
-
- expect(logger).to receive(:account_closed)
-
- account.close
- end
- end
- end
- """
- And a file named "lib/account.rb" with:
- """ruby
- Account = Struct.new(:logger) do
- def close
- logger.account_closed
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: expect a message with an argument
- Given a file named "spec/account_spec.rb" with:
- """ruby
- require "account"
-
- describe Account do
- context "when closed" do
- it "logs an account closed message" do
- logger = double("logger")
- account = Account.new logger
-
- expect(logger).to receive(:account_closed).with(account)
-
- account.close
- end
- end
- end
- """
- And a file named "lib/account.rb" with:
- """ruby
- Account = Struct.new(:logger) do
- def close
- logger.account_closed(self)
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: provide a return value
- Given a file named "spec/message_expectation_spec.rb" with:
- """ruby
-
- describe "a message expectation with a return value" do
- context "specified in a block" do
- it "returns the specified value" do
- object = double("object")
- expect(object).to receive(:message) { :return_value }
- expect(object.message).to eq(:return_value)
- end
- end
-
- context "specified with and_return" do
- it "returns the specified value" do
- object = double("object")
- expect(object).to receive(:message).and_return(:return_value)
- expect(object.message).to eq(:return_value)
- end
- end
- end
- """
- When I run `rspec spec/message_expectation_spec.rb`
- Then the output should contain "2 examples, 0 failures"
-
- Scenario: expect a specific number of calls
- Given a file named "spec/message_count_spec.rb" with:
- """ruby
- describe "a message expectation with a count" do
- it "passes if the expected number of calls happen" do
- string = "hi"
- expect(string).to receive(:length).exactly(3).times
-
- 3.times { string.length }
- end
- end
- """
- When I run `rspec spec/message_count_spec.rb`
- Then the output should contain "1 example, 0 failures"
View
118 features/message_expectations/expect_message_using_should_receive.feature
@@ -1,118 +0,0 @@
-Feature: expect message using `should_receive`
-
- Use `object.should_receive(:message)` to set an expectation that
- `object` should receive the message `:message` before the example is
- completed.
-
- Background:
- Given a file named "spec/spec_helper.rb" with:
- """ruby
- RSpec.configure do |config|
- config.mock_with :rspec do |mocks|
- mocks.syntax = :should
- end
- end
- """
-
- Scenario: expect a message
- Given a file named "spec/account_spec.rb" with:
- """ruby
- require "account"
- require "spec_helper"
-
- describe Account do
- context "when closed" do
- it "logs an account closed message" do
- logger = double("logger")
- account = Account.new logger
-
- logger.should_receive(:account_closed)
-
- account.close
- end
- end
- end
- """
- And a file named "lib/account.rb" with:
- """ruby
- Account = Struct.new(:logger) do
- def close
- logger.account_closed
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: expect a message with an argument
- Given a file named "spec/account_spec.rb" with:
- """ruby
- require "account"
- require "spec_helper"
-
- describe Account do
- context "when closed" do
- it "logs an account closed message" do
- logger = double("logger")
- account = Account.new logger
-
- logger.should_receive(:account_closed).with(account)
-
- account.close
- end
- end
- end
- """
- And a file named "lib/account.rb" with:
- """ruby
- Account = Struct.new(:logger) do
- def close
- logger.account_closed(self)
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: provide a return value
- Given a file named "spec/message_expectation_spec.rb" with:
- """ruby
- require "spec_helper"
-
- describe "a message expectation with a return value" do
- context "specified in a block" do
- it "returns the specified value" do
- object = double("object")
- object.should_receive(:message) { :return_value }
- expect(object.message).to eq(:return_value)
- end
- end
-
- context "specified with and_return" do
- it "returns the specified value" do
- object = double("object")
- object.should_receive(:message).and_return(:return_value)
- expect(object.message).to eq(:return_value)
- end
- end
- end
- """
- When I run `rspec spec/message_expectation_spec.rb`
- Then the output should contain "2 examples, 0 failures"
-
- Scenario: expect a specific number of calls
- Given a file named "spec/message_count_spec.rb" with:
- """ruby
- require "spec_helper"
-
- describe "a message expectation with a count" do
- it "passes if the expected number of calls happen" do
- string = "hi"
- string.should_receive(:length).exactly(3).times
-
- 3.times { string.length }
- end
- end
- """
- When I run `rspec spec/message_count_spec.rb`
- Then the output should contain "1 example, 0 failures"
View
49 features/message_expectations/message_chains_using_expect.feature
@@ -1,49 +0,0 @@
-Feature: Message chains in the expect syntax
-
- You can use `receive_message_chain` to stub nested calls
- on both partial and pure mock objects.
-
- Scenario: allow a chained message
- Given a file named "spec/chained_messages.rb" with:
- """ruby
- describe "a chained message expectation" do
- it "passes if the expected number of calls happen" do
- d = double
- allow(d).to receive_message_chain(:to_a, :length)
-
- d.to_a.length
- end
- end
- """
- When I run `rspec spec/chained_messages.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: allow a chained message with a return value
- Given a file named "spec/chained_messages.rb" with:
- """ruby
- describe "a chained message expectation" do
- it "passes if the expected number of calls happen" do
- d = double
- allow(d).to receive_message_chain(:to_a, :length).and_return(3)
-
- expect(d.to_a.length).to eq(3)
- end
- end
- """
- When I run `rspec spec/chained_messages.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: expect a chained message with a return value
- Given a file named "spec/chained_messages.rb" with:
- """ruby
- describe "a chained message expectation" do
- it "passes if the expected number of calls happen" do
- d = double
- expect(d).to receive_message_chain(:to_a, :length).and_return(3)
-
- expect(d.to_a.length).to eq(3)
- end
- end
- """
- When I run `rspec spec/chained_messages.rb`
- Then the output should contain "1 example, 0 failures"
View
209 features/message_expectations/receive_counts.feature
@@ -1,209 +0,0 @@
-Feature: receive counts
-
- Scenario: expect a message once
- Given a file named "spec/account_spec.rb" with:
- """ruby
- class Account
- attr_accessor :logger
-
- def open
- logger.account_opened
- end
- end
-
- describe Account do
- context "when opened" do
- it "logger#account_opened was called once" do
- logger = double("logger")
- account = Account.new
- account.logger = logger
-
- expect(logger).to receive(:account_opened).once
-
- account.open
- end
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: expect a message twice
- Given a file named "spec/account_spec.rb" with:
- """ruby
- class Account
- attr_accessor :logger
-
- def open
- logger.account_opened
- end
- end
-
- describe Account do
- context "when opened" do
- it "logger#account_opened was called once" do
- logger = double("logger")
- account = Account.new
- account.logger = logger
-
- expect(logger).to receive(:account_opened).twice
-
- account.open
- account.open
- end
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: expect a message 3 times
- Given a file named "spec/account_spec.rb" with:
- """ruby
- class Account
- attr_accessor :logger
-
- def open
- logger.account_opened
- end
- end
-
- describe Account do
- context "when opened" do
- it "logger#account_opened was called once" do
- logger = double("logger")
- account = Account.new
- account.logger = logger
-
- expect(logger).to receive(:account_opened).exactly(3).times
-
- account.open
- account.open
- account.open
- end
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: expect a message at least (:once)
- Given a file named "spec/account_spec.rb" with:
- """ruby
- class Account
- attr_accessor :logger
-
- def open
- logger.account_opened
- end
- end
-
- describe Account do
- context "when opened" do
- it "logger#account_opened was called once" do
- logger = double("logger")
- account = Account.new
- account.logger = logger
-
- expect(logger).to receive(:account_opened).at_least(:once)
-
- account.open
- end
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
-
- Scenario: expect a message at least (n) times
- Given a file named "spec/account_spec.rb" with:
- """ruby
- class Account
- attr_accessor :logger
-
- def open
- logger.account_opened
- end
- end
-
- describe Account do
- context "when opened" do
- it "logger#account_opened was called once" do
- logger = double("logger")
- account = Account.new
- account.logger = logger
-
- expect(logger).to receive(:account_opened).at_least(3).times
-
- # Note that I am calling method under test 4 times
- # and I specified it to be called at least 3 times
- account.open
- account.open
- account.open
- account.open
- end
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
-
- Scenario: expect a message at most (:once)
- Given a file named "spec/account_spec.rb" with:
- """ruby
- class Account
- attr_accessor :logger
-
- def open
- logger.account_opened
- end
- end
-
- describe Account do
- context "when opened" do
- it "logger#account_opened was called once" do
- logger = double("logger")
- account = Account.new
- account.logger = logger
-
- expect(logger).to receive(:account_opened).at_most(:once)
-
- account.open
- account.open
- end
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "expected: at most 1 time"
- And the output should contain "received: 2 times"
-
- Scenario: expect a message at most (n) times
- Given a file named "spec/account_spec.rb" with:
- """ruby
- class Account
- attr_accessor :logger
-
- def open
- logger.account_opened
- end
- end
-
- describe Account do
- context "when opened" do
- it "logger#account_opened was called once" do
- logger = double("logger")
- account = Account.new
- account.logger = logger
-
- expect(logger).to receive(:account_opened).at_most(2).times
-
- account.open
- account.open
- end
- end
- end
- """
- When I run `rspec spec/account_spec.rb`
- Then the output should contain "1 example, 0 failures"
View
50 features/message_expectations/warn_when_expectation_is_set_on_nil.feature
@@ -1,50 +0,0 @@
-Feature: warn when expectation is set on nil
-
- Scenario: nil instance variable
- Given a file named "example_spec.rb" with:
- """ruby
- RSpec.configure {|c| c.mock_with :rspec}
- describe "something" do
- it "does something" do
- expect(@i_do_not_exist).to receive(:foo)
- @i_do_not_exist.foo
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the output should contain "An expectation of :foo was set on nil"
-
- Scenario: allow
- Given a file named "example_spec.rb" with:
- """ruby
- RSpec.configure {|c| c.mock_with :rspec}
- describe "something" do
- it "does something" do
- allow_message_expectations_on_nil
- expect(nil).to receive(:foo)
- nil.foo
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the output should not contain "An expectation"
-
- Scenario: allow in one example, but not on another
- Given a file named "example_spec.rb" with:
- """ruby
- RSpec.configure {|c| c.mock_with :rspec}
- describe "something" do
- it "does something (foo)" do
- allow_message_expectations_on_nil
- nil.should_receive(:foo)
- nil.foo
- end
- it "does something (bar)" do
- expect(nil).to receive(:bar)
- nil.bar
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the output should contain "An expectation of :bar"
- And the output should not contain "An expectation of :foo"
View
77 features/method_stubs/README.md
@@ -1,77 +0,0 @@
-### Stub return values
-
- # create a double
- obj = double()
-
- # specify a return value using `:expect` syntax
- allow(obj).to receive(:message) { :value }
- allow(obj).to receive(:message).and_return(:value)
-
- # specify a return value using `:should` syntax
- obj.stub(:message) { :value }
- obj.stub(:message => :value)
- obj.stub(:message).and_return(:value)
-
-These forms are somewhat interchangeable. The difference is that the
-block contents are evaluated lazily when the `obj` receives the
-`message` message, whereas the others are evaluated as they are read.
-
-### Fake implementation
-
- allow(obj).to receive(:message) do |arg1, arg2|
- # set expectations about the args in this block
- # and/or return value
- end
-
- obj.stub(:message) do |arg1, arg2|
- # set expectations about the args in this block
- # and/or return a value
- end
-
-### Raising/Throwing
-
- allow(obj).to receive(:message).and_raise("this error")
- allow(obj).to receive(:message).and_throw(:this_symbol)
-
- obj.stub(:message).and_raise("this error")
- obj.stub(:message).and_throw(:this_symbol)
-
-You can also use the block format:
-
- allow(obj).to receive(:message) { raise "this error" }
- allow(obj).to receive(:message) { throw :this_symbol }
-
- obj.stub(:message) { raise "this error" }
- obj.stub(:message) { throw :this_symbol }
-
-### Argument constraints
-
-#### Explicit arguments
-
- allow(obj).to receive(:message).with('an argument') { ... }
- allow(obj).to receive(:message).with('more than', 'an argument') { ... }
-
- obj.stub(:message).with('an argument') { ... }
- obj.stub(:message).with('more_than', 'one_argument') { ... }
-
-#### Argument matchers
-
- allow(obj).to receive(:message).with(anything()) { ... }
- allow(obj).to receive(:message).with(an_instance_of(Money)) { ... }
- allow(obj).to receive(:message).with(hash_including(:a => 'b')) { ... }
- allow(obj).to receive(:message).with(array_including(1,2,3)) { ... }
- # or
- allow(obj).to receive(:message).with(array_including([1,2,3])) { ... }
-
- obj.stub(:message).with(anything()) { ... }
- obj.stub(:message).with(an_instance_of(Money)) { ... }
- obj.stub(:message).with(hash_including(:a => 'b')) { ... }
- obj.stub(:message).with(array_including(1,2,3)) { ... }
- # or
- obj.stub(:message).with(array_including([1,2,3])) { ... }
-
-#### Regular expressions
-
- allow(obj).to receive(:message).with(/abc/) { ... }
-
- obj.stub(:message).with(/abc/) { ... }
View
136 features/method_stubs/allow_any_instance_of.feature
@@ -1,136 +0,0 @@
-Feature: stub on any instance of a class
-
- Use `allow_any_instance_of` on a class to tell any instance of that class to
- return a value (or values) in response to a given message. If no instance
- receives the message, nothing happens.
-
- Messages can be stubbed on any class, including those in Ruby's core library.
-
- Note: You can use `allow_any_instance_of` when you don't have a reference
- to the object that receives a message in your test. For more information,
- see the message_expectations/allow_any_instance_of feature.
-
- Scenario: Stubbing any instance of an object with a single return value
- Given a file named "example_spec.rb" with:
- """ruby
- describe "stubbing any instance" do
- it "returns the specified value on any instance of the class" do
- allow_any_instance_of(Object).to receive(:foo).and_return(:return_value)
-
- o = Object.new
- expect(o.foo).to eq(:return_value)
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the examples should all pass
-
- Scenario: any_instance stub with a hash
- Given a file named "example_spec.rb" with:
- """ruby
- describe "any_instance.stub" do
- context "with a hash" do
- it "returns the hash values on any instance of the class" do
- allow_any_instance_of(Object).to receive_messages(:foo => 'foo', :bar => 'bar')
-
- o = Object.new
- expect(o.foo).to eq('foo')
- expect(o.bar).to eq('bar')
- end
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the examples should all pass
-
- Scenario: Stubbing any instance of an object with specific arguments matchers
- Given a file named "example_spec.rb" with:
- """ruby
- describe "stubbing any instance" do
- context "with arguments" do
- it "returns the stubbed value when arguments match" do
- allow_any_instance_of(Object).to receive(:foo).with(:param_one, :param_two).and_return(:result_one)
- allow_any_instance_of(Object).to receive(:foo).with(:param_three, :param_four).and_return(:result_two)
-
- o = Object.new
- expect(o.foo(:param_one, :param_two)).to eq(:result_one)
- expect(o.foo(:param_three, :param_four)).to eq(:result_two)
- end
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the examples should all pass
-
- Scenario: any_instance unstub
- Given a file named "example_spec.rb" with:
- """ruby
- describe "any_instance.unstub" do
- it "unstubs a stubbed method" do
- class Object
- def foo
- :foo
- end
- end
-
- Object.any_instance.stub(:foo)
- Object.any_instance.unstub(:foo)
-
- expect(Object.new.foo).to eq(:foo)
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the examples should all pass
-
- Scenario: any_instance unstub
- Given a file named "example_spec.rb" with:
- """ruby
- describe "any_instance.unstub" do
- context "with both an expectation and a stub already set" do
- it "only removes the stub" do
- class Object
- def foo
- :foo
- end
- end
- expect_any_instance_of(Object).to receive(:foo).and_return(:bar)
- Object.any_instance.stub(:foo)
- Object.any_instance.unstub(:foo)
-
- expect(Object.new.foo).to eq(:bar)
- end
- end
- end
- """
- When I run `rspec example_spec.rb`
- Then the examples should all pass
-
- Scenario: stub a chain of methods an any instance
- Given a file named "stub_chain_spec.rb" with:
- """ruby
- describe "stubbing a chain of methods" do
- context "given symbols representing methods" do
- it "returns the correct value" do
- allow_any_instance_of(Object).to receive_message_chain(:one, :two, :three).and_return(:four)
- expect(Object.new.one.two.three).to eq(:four)
- end
- end
-
- context "given a hash at the end" do
- it "returns the correct value" do
- allow_any_instance_of(Object).to receive_message_chain(:one, :two, :three=> :four)
- expect(Object.new.one.two.three).to eq(:four)
- end
- end
-
- context "given a string of methods separated by dots" do
- it "returns the correct value" do
- allow_any_instance_of(Object).to receive_message_chain("one.two.three").and_return(:four)
- expect(Object.new.one.two.three).to eq(:four)
- end
- end
- end
- """
- When I run `rspec stub_chain_spec.rb`
- Then the examples should all pass
View
40 features/method_stubs/as_null_object.feature
@@ -1,40 +0,0 @@
-Feature: as_null_object
-
- Use the `as_null_object` method to ignore any messages that aren't explicitly
- set as stubs or message expectations.
-
- EXCEPTION: `to_ary` will raise a NoMethodError unless explicitly stubbed in
- order to support `flatten` on an Array containing a double.
-
- Scenario: double acting as_null_object
- Given a file named "as_null_object_spec.rb" with:
- """ruby
- describe "a double with as_null_object called" do
- let(:null_object) { double('null object').as_null_object }
-
- it "responds to any method that is not defined" do
- expect(null_object).to respond_to(:an_undefined_method)
- end
-
- it "allows explicit stubs using expect syntax" do
- allow(null_object).to receive(:foo) { "bar" }
- expect(null_object.foo).to eq("bar")
- end
-
- it "allows explicit stubs using should syntax" do
- null_object.stub(:foo) { "bar" }
- expect(null_object.foo).to eq("bar")
- end
-
- it "allows explicit expectations" do
- expect(null_object).to receive(:something)
- null_object.something
- end
-
- it "supports Array#flatten" do
- expect([null_object].flatten).to eq([null_object])
- end
- end
- """
- When I run `rspec as_null_object_spec.rb`
- Then the examples should all pass