pax_global_header00006660000000000000000000000064145576703050014526gustar00rootroot0000000000000052 comment=5089089f5226cc1a68a4bb72b7394ced42f3de79 rspec-mocks-3.13.0/000077500000000000000000000000001455767030500140405ustar00rootroot00000000000000rspec-mocks-3.13.0/.autotest000066400000000000000000000002161455767030500157100ustar00rootroot00000000000000Autotest.add_hook :initialize do |at| at.clear_mappings at.add_mapping(%r%\.rb$%) { at.files_matching %r%^spec/.*_spec\.rb$% } end rspec-mocks-3.13.0/.document000066400000000000000000000000601455767030500156530ustar00rootroot00000000000000lib/**/*.rb - README.md LICENSE.md Changelog.md rspec-mocks-3.13.0/.github/000077500000000000000000000000001455767030500154005ustar00rootroot00000000000000rspec-mocks-3.13.0/.github/FUNDING.yml000066400000000000000000000003351455767030500172160ustar00rootroot00000000000000# This file was generated on 2023-04-16T20:53:23+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. github: [JonRowe, benoittgt] open_collective: rspec rspec-mocks-3.13.0/.github/dependabot.yml000066400000000000000000000004371455767030500202340ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" rspec-mocks-3.13.0/.github/workflows/000077500000000000000000000000001455767030500174355ustar00rootroot00000000000000rspec-mocks-3.13.0/.github/workflows/ci.yml000066400000000000000000000114301455767030500205520ustar00rootroot00000000000000# This file was generated on 2023-12-26T21:12:38+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. name: RSpec CI on: push: branches: - 'main' - '*-maintenance' - '*-dev' pull_request: branches: - '*' permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true env: RSPEC_CI: true # This tells rspec-rails what branch to run in ci RSPEC_VERSION: '~> 3.13.0' jobs: rubocop: name: Rubocop runs-on: 'ubuntu-20.04' steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: '3.0' - run: script/update_rubygems_and_install_bundler - run: script/clone_all_rspec_repos - run: bundle install --standalone - run: bundle binstubs --all - run: script/run_rubocop test: name: Ruby ${{ matrix.ruby }} ${{ matrix.name_extra || '' }} runs-on: ${{ matrix.os || 'ubuntu-20.04' }} strategy: matrix: ruby: - '3.3' - '3.2' - '3.1' - '3.0' - 2.7 - 2.6 - 2.5 - 2.4 - 2.3 - 2.2 env: - DIFF_LCS_VERSION: "> 1.4.3" include: - ruby: ruby-head env: RUBY_HEAD: true - ruby: jruby-9.2.13.0 env: JRUBY_OPTS: "--dev" - ruby: 2.7 name_extra: "with diff-lcs 1.3" env: DIFF_LCS_VERSION: "~> 1.3.0" - ruby: 2.7 name_extra: "with diff-lcs 1.4.3" env: DIFF_LCS_VERSION: "1.4.3" fail-fast: false continue-on-error: ${{ matrix.allow_failure || endsWith(matrix.ruby, 'head') }} env: ${{ matrix.env }} steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler: ${{ matrix.bundler || '2.2.22' }} ruby-version: ${{ matrix.ruby }} - run: script/update_rubygems_and_install_bundler - run: script/clone_all_rspec_repos - run: bundle install --standalone - run: bundle binstubs --all - run: script/run_build legacy: name: Legacy Ruby Builds (${{ matrix.container.version }}) runs-on: ubuntu-20.04 container: image: ${{ matrix.container.tag }} options: ${{ matrix.container.options || '--add-host github-complains-if-this-is-empty.com:127.0.0.1' }} strategy: fail-fast: false matrix: container: - version: "2.1.9" tag: ghcr.io/rspec/docker-ci:2.1.9 post: git config --global --add safe.directory `pwd` - version: "2.0" tag: ghcr.io/rspec/docker-ci:2.0.0 - version: "1.9.3" tag: ghcr.io/rspec/docker-ci:1.9.3 - version: "1.9.2" tag: ghcr.io/rspec/docker-ci:1.9.2 options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" - version: "1.8.7" tag: ghcr.io/rspec/docker-ci:1.8.7 options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" - version: "REE" tag: ghcr.io/rspec/docker-ci:ree options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" - version: "JRuby 1.7" tag: ghcr.io/rspec/docker-ci:jruby-1.7 - version: "JRuby 1.7 1.8 mode" tag: ghcr.io/rspec/docker-ci:jruby-1.7 jruby_opts: '--dev --1.8' pre: gem uninstall jruby-openssl options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" - version: "JRuby 9.1.17.0" tag: ghcr.io/rspec/docker-ci:jruby-9.1.17.0 options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" env: LEGACY_CI: true JRUBY_OPTS: ${{ matrix.container.jruby_opts || '--dev' }} steps: - uses: actions/checkout@v3 - run: ${{ matrix.container.pre }} - run: script/legacy_setup.sh - run: ${{ matrix.container.post }} - run: bundle exec bin/rspec - run: bundle exec script/cucumber.sh windows: name: Ruby ${{ matrix.ruby }} (Windows) runs-on: windows-latest strategy: matrix: ruby: - 2.7 - 2.6 - 2.5 - 2.4 - 2.3 - 2.2 fail-fast: false steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler: '2.2.22' ruby-version: ${{ matrix.ruby }} bundler-cache: true - run: choco install ansicon - run: bundle exec rspec --backtrace rspec-mocks-3.13.0/.gitignore000066400000000000000000000002451455767030500160310ustar00rootroot00000000000000*.sw? .DS_Store coverage rdoc pkg doc tmp rerun.txt Gemfile.lock .bundle .idea *.rbc .yardoc bin Gemfile-custom bundle .rbx .rspec-local spec/examples.txt specs.out rspec-mocks-3.13.0/.rspec000066400000000000000000000000411455767030500151500ustar00rootroot00000000000000--warnings --require spec_helper rspec-mocks-3.13.0/.rubocop.yml000066400000000000000000000007441455767030500163170ustar00rootroot00000000000000inherit_from: - .rubocop_todo.yml - .rubocop_rspec_base.yml # All these metrics should go down over time. Metrics/ClassLength: Max: 279 Metrics/CyclomaticComplexity: Max: 18 Layout/LineLength: Max: 193 Exclude: - features/**/* - spec/**/* Metrics/MethodLength: Max: 49 Metrics/AbcSize: Max: 47 Metrics/BlockLength: Max: 45 Exclude: - spec/**/* Metrics/ModuleLength: Max: 220 Exclude: - spec/**/* Metrics/PerceivedComplexity: Max: 19 rspec-mocks-3.13.0/.rubocop_rspec_base.yml000066400000000000000000000150051455767030500205010ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects # can customize by inheriting this file and overriding particular settings. Layout/AccessModifierIndentation: Enabled: false # "Use alias_method instead of alias" # We're fine with `alias`. Style/Alias: Enabled: false # "Avoid the use of the case equality operator ===" # We prefer using `Class#===` over `Object#is_a?` because `Class#===` # is less likely to be monkey patched than `is_a?` on a user object. Style/CaseEquality: Enabled: false # Warns when the class is excessively long. Metrics/ClassLength: Max: 100 Style/CollectionMethods: PreferredMethods: reduce: 'inject' # Over time we'd like to get this down, but this is what we're at now. Metrics/CyclomaticComplexity: Max: 10 # We use YARD to enforce documentation. It works better than rubocop's # enforcement...rubocop complains about the places we re-open # `RSpec::Expectations` and `RSpec::Matchers` w/o having doc commments. Style/Documentation: Enabled: false # We still support 1.8.7 which requires trailing dots Layout/DotPosition: EnforcedStyle: trailing Style/DoubleNegation: Enabled: false # each_with_object is unavailable on 1.8.7 so we have to disable this one. Style/EachWithObject: Enabled: false Style/FormatString: EnforcedStyle: percent # As long as we support ruby 1.8.7 we have to use hash rockets. Style/HashSyntax: EnforcedStyle: hash_rockets # We can't use the new lambda syntax, since we still support 1.8.7. Style/Lambda: Enabled: false # Over time we'd like to get this down, but this is what we're at now. Layout/LineLength: Max: 100 # Over time we'd like to get this down, but this is what we're at now. Metrics/MethodLength: Max: 15 # Who cares what we call the argument for binary operator methods? Naming/BinaryOperatorParameterName: Enabled: false Style/PercentLiteralDelimiters: PreferredDelimiters: '%': () # double-quoted string '%i': '[]' # array of symbols '%q': () # single-quoted string '%Q': () # double-quoted string '%r': '{}' # regular expression pattern '%s': () # a symbol '%w': '[]' # array of single-quoted strings '%W': '[]' # array of double-quoted strings '%x': () # a shell command as a string # We have too many special cases where we allow generator methods or prefer a # prefixed predicate due to it's improved readability. Naming/PredicateName: Enabled: false # On 1.8 `proc` is `lambda`, so we use `Proc.new` to ensure we get real procs on all supported versions. # http://batsov.com/articles/2014/02/04/the-elements-of-style-in-ruby-number-12-proc-vs-proc-dot-new/ Style/Proc: Enabled: false # Exceptions should be rescued with `Support::AllExceptionsExceptOnesWeMustNotRescue` Lint/RescueException: Enabled: true # We haven't adopted the `fail` to signal exceptions vs `raise` for re-raises convention. Style/SignalException: Enabled: false # We've tended to use no space, so it's less of a change to stick with that. Layout/SpaceAroundEqualsInParameterDefault: EnforcedStyle: no_space # We don't care about single vs double qoutes. Style/StringLiterals: Enabled: false # This rule favors constant names from the English standard library which we don't load. Style/SpecialGlobalVars: Enabled: false Style/TrailingCommaInArrayLiteral: Enabled: false Style/TrailingCommaInHashLiteral: Enabled: false Style/TrailingCommaInArguments: Enabled: false Style/TrivialAccessors: AllowDSLWriters: true AllowPredicates: true ExactNameMatch: true Style/ParallelAssignment: Enabled: false Layout/EmptyLineBetweenDefs: Enabled: false Layout/FirstParameterIndentation: Enabled: false Layout/ParameterAlignment: EnforcedStyle: with_first_parameter Layout/SpaceInsideBlockBraces: Enabled: false Layout/SpaceInsideParens: Enabled: false Naming/ConstantName: Enabled: false Style/ClassCheck: Enabled: false Style/ConditionalAssignment: Enabled: false Style/EmptyMethod: Enabled: false Style/FormatStringToken: Enabled: false Style/GuardClause: Enabled: false Style/IdenticalConditionalBranches: Enabled: false Style/IfUnlessModifier: Enabled: false Style/IfUnlessModifierOfIfUnless: Enabled: false Lint/MissingSuper: Enabled: false Style/MissingRespondToMissing: Enabled: false Style/MixinUsage: Enabled: false Style/MultipleComparison: Enabled: false Style/MutableConstant: Enabled: false Style/NestedModifier: Enabled: false Style/NestedParenthesizedCalls: Enabled: false Style/NumericPredicate: Enabled: false Style/RedundantParentheses: Enabled: false Style/StringLiteralsInInterpolation: Enabled: false Style/SymbolArray: Enabled: false Style/SymbolProc: Enabled: false Style/YodaCondition: Enabled: false Style/ZeroLengthPredicate: Enabled: false Layout/ClosingParenthesisIndentation: Enabled: false Layout/ExtraSpacing: Enabled: false Layout/MultilineMethodCallBraceLayout: Enabled: false Layout/MultilineMethodCallIndentation: Enabled: false Layout/MultilineOperationIndentation: Enabled: false Layout/SpaceAroundBlockParameters: Enabled: false Layout/SpaceAroundOperators: Enabled: false Layout/SpaceBeforeComma: Enabled: false Style/BlockDelimiters: Enabled: false Style/EmptyCaseCondition: Enabled: false Style/MultilineIfModifier: Enabled: false Style/RescueStandardError: Enabled: false Style/StderrPuts: Enabled: false Style/TernaryParentheses: Enabled: false Naming/HeredocDelimiterNaming: Enabled: false Layout/AssignmentIndentation: Enabled: false Layout/EmptyLineAfterMagicComment: Enabled: false Layout/FirstArrayElementIndentation: Enabled: false Layout/HeredocIndentation: Enabled: false Layout/SpaceInsidePercentLiteralDelimiters: Enabled: false Style/EmptyElse: Enabled: false Style/IfInsideElse: Enabled: false Style/RedundantReturn: Enabled: false Style/StructInheritance: Enabled: false Naming/VariableNumber: Enabled: false Layout/SpaceInsideStringInterpolation: Enabled: false Style/DateTime: Enabled: false Style/ParenthesesAroundCondition: Enabled: false Layout/EmptyLinesAroundBlockBody: Enabled: false Lint/ImplicitStringConcatenation: Enabled: false Lint/NestedMethodDefinition: Enabled: false Style/RegexpLiteral: Enabled: false Style/TrailingUnderscoreVariable: Enabled: false Layout/EmptyLinesAroundAccessModifier: Enabled: false rspec-mocks-3.13.0/.rubocop_todo.yml000066400000000000000000000430701455767030500173430ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2022-01-09 17:32:27 UTC using RuboCop version 1.11.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 1 # Configuration parameters: Include. # Include: **/*.gemfile, **/Gemfile, **/gems.rb Bundler/DuplicatedGem: Exclude: - 'Gemfile' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. # Include: **/*.gemfile, **/Gemfile, **/gems.rb Bundler/OrderedGems: Exclude: - 'Gemfile' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. # Include: **/*.gemspec Gemspec/OrderedDependencies: Exclude: - 'rspec-mocks.gemspec' # Offense count: 1 # Configuration parameters: Include. # Include: **/*.gemspec Gemspec/RequiredRubyVersion: Exclude: - 'rspec-mocks.gemspec' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_argument, with_fixed_indentation Layout/ArgumentAlignment: Exclude: - 'spec/rspec/mocks/should_syntax_spec.rb' - 'spec/rspec/mocks_spec.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleAlignWith. # SupportedStylesAlignWith: either, start_of_block, start_of_line Layout/BlockAlignment: Exclude: - 'spec/rspec/mocks/any_instance_spec.rb' # Offense count: 37 # Cop supports --auto-correct. Layout/EmptyLineAfterGuardClause: Enabled: false # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: AllowAliasSyntax, AllowedMethods. # AllowedMethods: alias_method, public, protected, private Layout/EmptyLinesAroundAttributeAccessor: Exclude: - 'spec/rspec/mocks/double_spec.rb' - 'spec/rspec/mocks/partial_double_spec.rb' # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses Layout/FirstArgumentIndentation: Exclude: - 'lib/rspec/mocks/error_generator.rb' - 'spec/rspec/mocks/nil_expectation_warning_spec.rb' - 'spec/rspec/mocks/partial_double_spec.rb' - 'spec/rspec/mocks/stubbed_message_expectations_spec.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table # SupportedColonStyles: key, separator, table # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit Layout/HashAlignment: Exclude: - 'lib/rspec/mocks/method_double.rb' - 'rspec-mocks.gemspec' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: normal, indented_internal_methods Layout/IndentationConsistency: Exclude: - 'spec/rspec/mocks/any_instance_spec.rb' - 'spec/rspec/mocks_spec.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: Width, IgnoredPatterns. Layout/IndentationWidth: Exclude: - 'lib/rspec/mocks/any_instance/expectation_chain.rb' - 'lib/rspec/mocks/method_reference.rb' - 'lib/rspec/mocks/test_double.rb' - 'spec/rspec/mocks/matchers/receive_spec.rb' - 'spec/rspec/mocks_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Layout/LeadingEmptyLines: Exclude: - 'spec/rspec/mocks/space_spec.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https Layout/LineLength: Max: 325 # Offense count: 3 # Cop supports --auto-correct. Layout/SpaceAfterComma: Exclude: - 'spec/rspec/mocks/array_including_matcher_spec.rb' # Offense count: 43 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideHashLiteralBraces: Exclude: - 'spec/rspec/mocks/argument_matchers_spec.rb' - 'spec/rspec/mocks/diffing_spec.rb' - 'spec/rspec/mocks/hash_excluding_matcher_spec.rb' - 'spec/rspec/mocks/hash_including_matcher_spec.rb' - 'spec/rspec/mocks/should_syntax_spec.rb' # Offense count: 44 Lint/AmbiguousBlockAssociation: Exclude: - 'spec/rspec/mocks/and_call_original_spec.rb' - 'spec/rspec/mocks/and_wrap_original_spec.rb' - 'spec/rspec/mocks/any_instance_spec.rb' - 'spec/rspec/mocks/argument_matchers_spec.rb' - 'spec/rspec/mocks/block_return_value_spec.rb' - 'spec/rspec/mocks/combining_implementation_instructions_spec.rb' - 'spec/rspec/mocks/double_spec.rb' - 'spec/rspec/mocks/matchers/receive_spec.rb' - 'spec/rspec/mocks/serialization_spec.rb' - 'spec/rspec/mocks/space_spec.rb' - 'spec/rspec/mocks/stub_spec.rb' - 'spec/rspec/mocks/stubbed_message_expectations_spec.rb' # Offense count: 3 # Cop supports --auto-correct. Lint/AmbiguousRegexpLiteral: Exclude: - 'features/step_definitions/additional_cli_steps.rb' # Offense count: 20 # Configuration parameters: AllowedMethods. # AllowedMethods: enums Lint/ConstantDefinitionInBlock: Exclude: - 'spec/rspec/mocks/any_instance_spec.rb' - 'spec/rspec/mocks/argument_matchers_spec.rb' - 'spec/rspec/mocks/double_spec.rb' - 'spec/rspec/mocks/expiration_spec.rb' - 'spec/rspec/mocks/instance_method_stasher_spec.rb' - 'spec/rspec/mocks/marshal_extension_spec.rb' - 'spec/rspec/mocks/matchers/have_received_spec.rb' - 'spec/rspec/mocks/mock_expectation_error_spec.rb' - 'spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb' - 'spec/rspec/mocks/serialization_spec.rb' - 'spec/rspec/mocks/spy_spec.rb' - 'spec/rspec/mocks/stub_spec.rb' - 'spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb' - 'spec/rspec/mocks/verifying_doubles/construction_spec.rb' - 'spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb' # Offense count: 1 Lint/DuplicateRequire: Exclude: - 'spec/spec_helper.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: runtime_error, standard_error Lint/InheritException: Exclude: - 'lib/rspec/mocks/error_generator.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/RedundantStringCoercion: Exclude: - 'spec/rspec/mocks/message_expectation_string_representation_spec.rb' # Offense count: 2 Lint/RescueException: Exclude: - 'spec/rspec/mocks/should_syntax_spec.rb' - 'spec/rspec/mocks_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/SendWithMixinArgument: Exclude: - 'lib/rspec/mocks/minitest_integration.rb' # Offense count: 1 Lint/ShadowingOuterLocalVariable: Exclude: - 'benchmarks/each_value_v_values_each.rb' # Offense count: 2 Lint/StructNewOverride: Exclude: - 'spec/rspec/mocks/and_call_original_spec.rb' - 'spec/rspec/mocks/any_instance_spec.rb' # Offense count: 5 # Configuration parameters: AllowComments. Lint/SuppressedException: Exclude: - 'spec/rspec/mocks/any_instance_spec.rb' - 'spec/rspec/mocks/mock_expectation_error_spec.rb' - 'spec/rspec/mocks/should_syntax_spec.rb' - 'spec/rspec/mocks_spec.rb' - 'spec/spec_helper.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. Lint/UselessAccessModifier: Exclude: - 'lib/rspec/mocks/method_double.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: AllowComments. Lint/UselessMethodDefinition: Exclude: - 'lib/rspec/mocks/verifying_message_expectation.rb' # Offense count: 1 # Configuration parameters: IgnoredMethods, CountRepeatedAttributes. Metrics/AbcSize: Max: 47 # Offense count: 100 # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. # IgnoredMethods: refine Metrics/BlockLength: Max: 1066 # Offense count: 8 # Configuration parameters: CountComments, CountAsOne. Metrics/ModuleLength: Max: 1066 # Offense count: 23 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. # AllowedNames: at, by, db, id, in, io, ip, of, on, os, pp, to Naming/MethodParameterName: Exclude: - 'lib/rspec/mocks/message_expectation.rb' - 'spec/rspec/mocks/and_call_original_spec.rb' - 'spec/rspec/mocks/any_instance_spec.rb' - 'spec/rspec/mocks/partial_double_spec.rb' - 'spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb' - 'spec/rspec/mocks/should_syntax_spec.rb' - 'spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb' - 'spec/support/doubled_classes.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: PreferredName. Naming/RescuedExceptionsVariableName: Exclude: - 'lib/rspec/mocks/error_generator.rb' - 'spec/rspec/mocks/matchers/receive_messages_spec.rb' - 'spec/rspec/mocks_spec.rb' # Offense count: 2 Security/Eval: Exclude: - 'Gemfile' - 'spec/rspec/mocks/verifying_doubles/method_visibility_spec.rb' # Offense count: 5 Security/MarshalLoad: Exclude: - 'spec/rspec/mocks/marshal_extension_spec.rb' # Offense count: 4 # Configuration parameters: EnforcedStyle, AllowModifiersOnSymbols. # SupportedStyles: inline, group Style/AccessModifierDeclarations: Exclude: - 'benchmarks/method_defined_at_any_visibility.rb' - 'spec/rspec/mocks/stub_spec.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: separated, grouped Style/AccessorGrouping: Exclude: - 'lib/rspec/mocks/message_expectation.rb' - 'lib/rspec/mocks/proxy.rb' # Offense count: 10 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: percent_q, bare_percent Style/BarePercentLiterals: Exclude: - 'spec/rspec/mocks/double_spec.rb' - 'spec/rspec/mocks/matchers/receive_messages_spec.rb' - 'spec/rspec/mocks/partial_double_spec.rb' - 'spec/rspec/mocks/stubbed_message_expectations_spec.rb' - 'spec/rspec/mocks/verifying_doubles/naming_spec.rb' # Offense count: 3 # Cop supports --auto-correct. Style/BlockComments: Exclude: - 'benchmarks/find_original_method_early.rb' - 'benchmarks/method_defined_at_any_visibility.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Exclude: - 'spec/rspec/mocks/any_instance_spec.rb' - 'spec/rspec/mocks/matchers/receive_message_chain_spec.rb' - 'spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb' - 'spec/rspec/mocks/space_spec.rb' # Offense count: 1 Style/CombinableLoops: Exclude: - 'benchmarks/allocations/1_object_1000_mocks.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: Keywords. # Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE Style/CommentAnnotation: Exclude: - 'spec/rspec/mocks/and_call_original_spec.rb' - 'spec/rspec/mocks/double_spec.rb' - 'spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Style/CommentedKeyword: Exclude: - 'spec/rspec/mocks/matchers/receive_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Style/EachForSimpleLoop: Exclude: - 'spec/rspec/mocks/double_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Style/Encoding: Exclude: - 'rspec-mocks.gemspec' # Offense count: 9 # Cop supports --auto-correct. Style/EvalWithLocation: Exclude: - 'spec/rspec/mocks/and_yield_spec.rb' - 'spec/rspec/mocks/double_spec.rb' - 'spec/rspec/mocks/should_syntax_spec.rb' - 'spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb' - 'spec/rspec/mocks/verifying_doubles/method_visibility_spec.rb' - 'spec/support/doubled_classes.rb' # Offense count: 6 # Cop supports --auto-correct. Style/ExpandPathArguments: Exclude: - 'Gemfile' - 'benchmarks/allocations/helper.rb' - 'benchmarks/double_creation.rb' - 'benchmarks/thread_safety.rb' - 'benchmarks/transfer_nested_constants.rb' - 'rspec-mocks.gemspec' # Offense count: 2 # Cop supports --auto-correct. Style/ExplicitBlockArgument: Exclude: - 'benchmarks/allocations/helper.rb' - 'spec/rspec/mocks/partial_double_spec.rb' # Offense count: 134 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false # Offense count: 3 # Configuration parameters: AllowedVariables. Style/GlobalVars: Exclude: - 'benchmarks/find_original_method_early.rb' - 'spec/rspec/mocks/configuration_spec.rb' - 'spec/spec_helper.rb' # Offense count: 2 # Cop supports --auto-correct. Style/HashEachMethods: Exclude: - 'benchmarks/each_value_v_values_each.rb' - 'lib/rspec/mocks/space.rb' # Offense count: 1 # Cop supports --auto-correct. Style/HashTransformValues: Exclude: - 'lib/rspec/mocks/error_generator.rb' # Offense count: 3 Style/MultilineBlockChain: Exclude: - 'spec/rspec/mocks/failure_notification_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Style/MultilineTernaryOperator: Exclude: - 'spec/rspec/mocks/null_object_double_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Style/MultilineWhenThen: Exclude: - 'lib/rspec/mocks/error_generator.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedOctalStyle. # SupportedOctalStyles: zero_with_o, zero_only Style/NumericLiteralPrefix: Exclude: - 'spec/rspec/mocks/error_generator_spec.rb' # Offense count: 11 # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Exclude: - 'lib/rspec/mocks/any_instance/proxy.rb' - 'lib/rspec/mocks/error_generator.rb' - 'lib/rspec/mocks/object_reference.rb' - 'lib/rspec/mocks/space.rb' - 'lib/rspec/mocks/test_double.rb' - 'lib/rspec/mocks/verifying_double.rb' - 'spec/rspec/mocks/partial_double_spec.rb' - 'spec/support/doubled_classes.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: lower_case_q, upper_case_q Style/PercentQLiterals: Exclude: - 'spec/rspec/mocks/stubbed_message_expectations_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Style/RedundantInterpolation: Exclude: - 'spec/rspec/mocks/double_spec.rb' # Offense count: 9 # Cop supports --auto-correct. Style/RedundantPercentQ: Exclude: - 'features/step_definitions/additional_cli_steps.rb' - 'spec/rspec/mocks/double_spec.rb' - 'spec/rspec/mocks/partial_double_spec.rb' - 'spec/rspec/mocks/stubbed_message_expectations_spec.rb' - 'spec/rspec/mocks/verifying_doubles/naming_spec.rb' # Offense count: 27 # Cop supports --auto-correct. Style/RedundantRegexpEscape: Exclude: - 'spec/rspec/mocks/argument_matchers_spec.rb' - 'spec/rspec/mocks/diffing_spec.rb' - 'spec/rspec/mocks/partial_double_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Style/RescueModifier: Exclude: - 'spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb' # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - 'lib/rspec/mocks/method_reference.rb' - 'lib/rspec/mocks/proxy.rb' # Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: AllowAsExpressionSeparator. Style/Semicolon: Exclude: - 'benchmarks/each_value_v_values_each.rb' - 'spec/rspec/mocks/double_spec.rb' - 'spec/rspec/mocks/failure_notification_spec.rb' - 'spec/rspec/mocks/instance_method_stasher_spec.rb' - 'spec/rspec/mocks/matchers/receive_messages_spec.rb' - 'spec/rspec/mocks/matchers/receive_spec.rb' - 'spec/rspec/mocks/reraising_eager_raises_spec.rb' - 'spec/rspec/mocks/stub_spec.rb' - 'spec/rspec/mocks/stubbed_message_expectations_spec.rb' # Offense count: 46 # Cop supports --auto-correct. # Configuration parameters: AllowIfMethodIsEmpty. Style/SingleLineMethods: Exclude: - 'spec/rspec/mocks/and_call_original_spec.rb' - 'spec/rspec/mocks/and_yield_spec.rb' - 'spec/rspec/mocks/any_instance_spec.rb' - 'spec/rspec/mocks/instance_method_stasher_spec.rb' - 'spec/rspec/mocks/matchers/receive_spec.rb' - 'spec/rspec/mocks/should_syntax_spec.rb' - 'spec/rspec/mocks/stub_implementation_spec.rb' - 'spec/rspec/mocks/stub_spec.rb' # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: AllowModifier. Style/SoleNestedConditional: Exclude: - 'lib/rspec/mocks/any_instance/message_chains.rb' - 'lib/rspec/mocks/any_instance/recorder.rb' - 'lib/rspec/mocks/argument_list_matcher.rb' - 'lib/rspec/mocks/proxy.rb' # Offense count: 5 # Cop supports --auto-correct. Style/StringConcatenation: Exclude: - 'lib/rspec/mocks/argument_matchers.rb' - 'lib/rspec/mocks/error_generator.rb' - 'lib/rspec/mocks/mutate_const.rb' - 'spec/rspec/mocks/block_return_value_spec.rb' - 'spec/rspec/mocks/mutate_const_spec.rb' rspec-mocks-3.13.0/.yardopts000066400000000000000000000001141455767030500157020ustar00rootroot00000000000000--exclude features --no-private --markup markdown - Changelog.md LICENSE.md rspec-mocks-3.13.0/BUILD_DETAIL.md000066400000000000000000000124411455767030500162450ustar00rootroot00000000000000 # The CI build, in detail The [Travis CI build](https://travis-ci.org/rspec/rspec-mocks) runs many verification steps to prevent regressions and ensure high-quality code. To run the Travis build locally, run: ``` $ script/run_build ``` It can be useful to run the build steps individually to repro a failing part of a Travis build. Let's break the build down into the individual steps. ## Specs RSpec dogfoods itself. Its primary defense against regressions is its spec suite. Run with: ``` $ bundle exec rspec # or, if you installed your bundle with `--standalone --binstubs`: $ bin/rspec ``` The spec suite performs a couple extra checks that are worth noting: * *That all the code is warning-free.* Any individual example that produces output to `stderr` will fail. We also have a spec that loads all the `lib` and `spec` files in a newly spawned process to detect load-time warnings and fail if there are any. RSpec must be warning-free so that users who enable Ruby warnings will not get warnings from our code. * *That only a minimal set of stdlibs are loaded.* Since Ruby makes loaded libraries available for use in any context, we want to minimize how many bits of the standard library we load and use. Otherwise, RSpec's use of part of the standard library could mask a problem where a gem author forgets to load a part of the standard library they rely on. The spec suite contains a spec that defines a list of allowed loaded stdlibs. In addition, we use [SimpleCov](https://github.com/colszowka/simplecov) to measure and enforce test coverage. If the coverage falls below a project-specific threshold, the build will fail. ## Cukes RSpec uses [cucumber](https://cucumber.io/) for both acceptance testing and [documentation](https://rspec.info/documentation). Since we publish our cukes as documentation, please limit new cucumber scenarios to user-facing examples that help demonstrate usage. Any tests that exist purely to prevent regressions should be written as specs, even if they are written in an acceptance style. Duplication between our YARD API docs and the cucumber documentation is fine. Run with: ``` $ bundle exec cucumber # or, if you installed your bundle with `--standalone --binstubs`: $ bin/cucumber ``` ## YARD documentation RSpec uses [YARD](https://yardoc.org/) for API documentation on the [rspec.info site](https://rspec.info/). Our commitment to [SemVer](https://semver.org) requires that we explicitly declare our public API, and our build uses YARD to ensure that every class, module and method has either been labeled `@private` or has at least some level of documentation. For new APIs, this forces us to make an intentional decision about whether or not it should be part of RSpec's public API or not. To run the YARD documentation coverage check, run: ``` $ bundle exec yard stats --list-undoc # or, if you installed your bundle with `--standalone --binstubs`: $ bin/yard stats --list-undoc ``` We also want to prevent YARD errors or warnings when actually generating the docs. To check for those, run: ``` $ bundle exec yard doc --no-cache # or, if you installed your bundle with `--standalone --binstubs`: $ bin/yard doc --no-cache ``` ## RuboCop We use [RuboCop](https://github.com/rubocop-hq/rubocop) to enforce style conventions on the project so that the code has stylistic consistency throughout. Run with: ``` $ bundle exec rubocop lib # or, if you installed your bundle with `--standalone --binstubs`: $ bin/rubocop lib ``` Our RuboCop configuration is a work-in-progress, so if you get a failure due to a RuboCop default, feel free to ask about changing the configuration. Otherwise, you'll need to address the RuboCop failure, or, as a measure of last resort, by wrapping the offending code in comments like `# rubocop:disable SomeCheck` and `# rubocop:enable SomeCheck`. ## Run spec files one-by-one A fast TDD cycle depends upon being able to run a single spec file, without the rest of the test suite. While rare, it's fairly easy to create a situation where a spec passes when the entire suite runs but fails when its individual file is run. To guard against this, our CI build runs each spec file individually, using a bit of bash like: ``` for file in `find spec -iname '*_spec.rb'`; do echo "Running $file" bin/rspec $file -b --format progress done ``` Since this step boots RSpec so many times, it runs much, much faster when we can avoid the overhead of bundler. This is a main reason our CI build installs the bundle with `--standalone --binstubs` and runs RSpec via `bin/rspec` rather than `bundle exec rspec`. ## Running the spec suite for each of the other repos While each of the RSpec repos is an independent gem (generally designed to be usable on its own), there are interdependencies between the gems, and the specs for each tend to use features from the other gems. We don't want to merge a pull request for one repo that might break the build for another repo, so our CI build includes a spec that runs the spec suite of each of the _other_ project repos. Note that we only run the spec suite, not the full build, of the other projects, as the spec suite runs very quickly compared to the full build. rspec-mocks-3.13.0/CODE_OF_CONDUCT.md000066400000000000000000000054441455767030500166460ustar00rootroot00000000000000 # Contributor Code of Conduct For the purpose of building a welcoming, harassment-free community that values contributions from anyone, the RSpec project has adopted the following code of conduct. All contributors and participants (including maintainers!) are expected to abide by its terms. As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery * Personal attacks * Trolling or insulting/derogatory comments * Public or private harassment * Publishing other's private information, such as physical or electronic addresses, without explicit permission * Other unethical or unprofessional conduct Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting one of the project maintainers listed at https://rspec.info/about/. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/3/0/ rspec-mocks-3.13.0/CONTRIBUTING.md000066400000000000000000000103241455767030500162710ustar00rootroot00000000000000 # Contributing RSpec is a community-driven project that has benefited from improvements from over *500* contributors. We welcome contributions from *everyone*. While contributing, please follow the project [code of conduct](CODE_OF_CONDUCT.md), so that everyone can be included. If you'd like to help make RSpec better, here are some ways you can contribute: - by running RSpec HEAD to help us catch bugs before new releases - by [reporting bugs you encounter](https://github.com/rspec/rspec-mocks/issues/new) with [report template](#report-template) - by [suggesting new features](https://github.com/rspec/rspec-mocks/issues/new) - by improving RSpec's Feature or API [documentation](https://rspec.info/documentation/) - by improving [RSpec's website](https://rspec.info/) ([source](https://github.com/rspec/rspec.github.io)) - by taking part in [feature and issue discussions](https://github.com/rspec/rspec-mocks/issues) - by adding a failing test for reproducible [reported bugs](https://github.com/rspec/rspec-mocks/issues) - by reviewing [pull requests](https://github.com/rspec/rspec-mocks/pulls) and suggesting improvements - by [writing code](DEVELOPMENT.md) (no patch is too small! fix typos or bad whitespace) If you need help getting started, check out the [DEVELOPMENT](DEVELOPMENT.md) file for steps that will get you up and running. Thanks for helping us make RSpec better! ## `Small` issues These issue are ones that we be believe are best suited for new contributors to get started with. They represent a meaningful contribution to the project that should not be too hard to pull off. ## Report template Having a way to reproduce your issue will be very helpful for others to help confirm, investigate and ultimately fix your issue. You can do this by providing an executable test case. To make this process easier, we have prepared one basic [bug report templates](REPORT_TEMPLATE.md) for you to use as a starting point. ## Maintenance branches Maintenance branches are how we manage the different supported point releases of RSpec. As such, while they might look like good candidates to merge into main, please do not open pull requests to merge them. ## Working on multiple RSpec gems at the same time RSpec is composed of multiple gems (`rspec-core`, `rspec-mocks`, etc). Sometimes you have to work on a combination of them at the same time. When submitting your code for review, we ask that you get a passing build (green CI). If you are working across the repositories, please add a commit that temporarily pins your PR to the right branch of the other repository you depend on. For example, if we wanted a change in `rspec-expectations` that relied on a change for on `rspec-mocks`. We add a commit with the title: >[WIP] Use rspec-mocks with "custom-failure-message" branch And content: ```diff diff --git a/Gemfile b/Gemfile -%w[rspec rspec-core rspec-mocks rspec-support].each do |lib| +%w[rspec rspec-core rspec-support].each do |lib| library_path = File.expand_path("../../#{lib}", __FILE__) if File.exist?(library_path) && !ENV['USE_GIT_REPOS'] gem lib, :path => library_path @@ -11,6 +11,7 @@ branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => branch end end +gem 'rspec-mocks', :git => "https://github.com/rspec/rspec-mocks.git", :branch => "custom-failure-message" ``` In general the process is: 1. Create PRs explaining what you are trying to achieve. 2. Pin the repositories to each other. 3. Check they pass (go green). 4. Await review if appropriate. 5. Remove the commit from step 2. We will merge ignoring the failure. 6. Remove the commit from the other, check it passes with the other commit now on `main`. 7. Merge the other. 8. We will trigger builds for the `main` branch of affected repositories to check if everything is in order. Steps 5-8 should happen continuously (e.g. one after another but within a short timespan) so that we don't leave a broken main around. It is important to triage that build process and revert if necessary. rspec-mocks-3.13.0/Changelog.md000066400000000000000000001441321455767030500162560ustar00rootroot00000000000000### Development [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.13.0...main) ### 3.13.0 / 2024-02-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.7...v3.13.0) Enhancements: * Add an `array_excluding` matcher for arguments. (Zane Wolfgang Pickett, #1528) ### 3.12.7 / 2024-02-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.6...v3.12.7) Bug Fixes: * Reduce allocations from "any_instance" style mocks. (Carlos Palhares, #1479) ### 3.12.6 / 2023-07-11 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.5...v3.12.6) Bug Fixes: * Fix an issue with `and_call_original` when using the `method_missing` fallback with keyword arguments. (Igor Drozdov, #1552) ### 3.12.5 / 2023-03-30 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.4...v3.12.5) Bug Fixes: * Fix compatibility issue with Rails where active_support monkey patches `with` when using any instance. (Lachlan Sylvester, #1540) ### 3.12.4 / 2023-03-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.3...v3.12.4) Bug Fixes: * Fix an issue with asserting that Array#reverse is never called. (Brad Trick, #1533) * Fix compatibility issue with Rails where active_support monkey patches `with`. (Jean Boussier, #1531, #1534) ### 3.12.3 / 2023-01-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.2...v3.12.3) Bug Fixes: * Fix keyword delegation in `send` for verifying doubles on Ruby 3. (Charlie Honig, #1485) ### 3.12.2 / 2023-01-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.1...v3.12.2) Bug Fixes: * Fix implementation blocks for mocks using keyword arguments on Ruby 3.2.0. (Adam Steel, #1508) * Fix keyword argument assertions when mocking using `with` on Ruby 3.2.0. (Slava Kardakov, Benoit Tigeot, Phil Pirozhkov, Benoit Daloze, #1514) ### 3.12.1 / 2022-12-10 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.0...v3.12.1) Bug Fixes: * Remove empty diff marker when a diff only contains console codes. (Jon Rowe, #1506) * Show keyword vs hash diff marker when arguments are not `==` (Jon Rowe, #1506) * Change check to detect frozen objects to rescue errors rather than pre-empting by checking `frozen?` due to some objects mis-behaving. (Keegan Roth, #1401) * Prevent unfulfilled expectations using `expect_any_instance_of` across a class inheritance boundary from raising rather than failing. (Jon Rowe, #1496) * Prevent a misleading error message when using `allow(...).not_to` with unsupported matchers. (Phil Pirozhkov, #1503) ### 3.12.0 / 2022-10-26 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.2...v3.12.0) Enhancements: * Improve diff output when diffing keyword arguments against hashes. (Jean Boussier, #1461) ### 3.11.2 / 2022-10-25 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.1...v3.11.2) Bug Fixes: * Use the original implementation of `Class.new` to detect overridden definitions of `new` rather than the owner, fixing detection of "double aliased" methods in Ruby 3 and above. (Benoit Daloze, #1470, #1476) * Support keyword argument semantics when constraining argument expectations using `with` on Ruby 3.0+ with `instance_double` (Andrii Malyshko, #1473) ### 3.11.1 / 2022-03-31 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.0...v3.11.1) Bug Fixes: * Add extra `ruby2_keywords` calls to properly designate methods using `*args` to pass keyword around, fixes an issue with TruffleRuby. (Benoit Daloze, #1464) ### 3.11.0 / 2022-02-09 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.3...v3.11.0) Enhancements: * Add `and_invoke` implementation for configuring responses to `receive` (and `receive_messages`) with multiple callable objects. (Kyle Smith, #1411) ### 3.10.3 / 2022-01-28 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.2...v3.10.3) Bug Fixes: * Suppress warning by setting `$VERBOSE` to nil. (Nobuyoshi Nakada, #1414) * Support keyword argument semantics when constraining argument expectations using `with` on Ruby 3.0+ (Yusuke Endoh, #1394) ### 3.10.2 / 2021-01-27 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.1...v3.10.2) Bug Fixes: * Support keyword arguments with `and_call_original` on Ruby 3.0. (Bryan Powell, #1385) * `RSpec::Mocks::Constant#previously_defined?` is now always a boolean. (Phil Pirozhkov, #1397) * Support keyword arguments on Ruby 3.0 when used with `expect_any_instance_of` or `allow_any_instance_of` with `and_call_original`. (Jess Hottenstein, #1407) ### 3.10.1 / 2020-12-27 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.0...v3.10.1) Bug Fixes: * Issue `ArgumentError` rather than `TypeError` when unsupported methods on unsupported objects are attempted to be stubbed. (@zhisme, #1357) ### 3.10.0 / 2020-10-30 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.1...v3.10.0) Enhancements: * Add the ability to set a custom error generator in `MessageExpectation`. This will allow rspec-expectations to inject a custom failure message. (Benoit Tigeot and Nicolas Zermati, #1312) * Return the result of the block passed to `RSpec::Mocks.with_temporary_scope` when block run. (@expeehaa, #1329) ### 3.9.1 / 2019-12-31 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.0...v3.9.1) Bug Fixes: * Trigger `RSpec::Mocks.configuration.verifying_double_callbacks` when using `allow_any_instance_of` or `expect_any_instance_of` (Daniel Orner, #1309) ### 3.9.0 / 2019-10-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.2...v3.9.0) Enhancements: * Improve thread safety of message expectations by using Mutex to prevent deadlocking errors. (Ry Biesemeyer, #1236) * Add the ability to use `time` as an alias for `times`. For example: `expect(Class).to receive(:method).exactly(1).time`. (Pistos, Benoit Tigeot, #1271) ### 3.8.2 / 2019-10-02 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.1...v3.8.2) * Allow `array_including` argument matchers to be nested. (Emmanuel Delmas, #1291) ### 3.8.1 / 2019-06-13 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.8.0...v3.8.1) Bug Fixes: * Ensure stubbing methods does not change their visibility. (Kevin Boschert, #1277) ### 3.8.0 / 2018-08-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.7.0...v3.8.0) Bug Fixes: * Issue error when encountering invalid "counted" negative message expectations. (Sergiy Yarinovskiy, #1212) * Ensure `allow_any_instance_of` and `expect_any_instance_of` can be temporarily supressed. (Jon Rowe, #1228) * Ensure `expect_any_instance_of(double).to_not have_received(:some_method)` fails gracefully (as its not supported) rather than issuing a `NoMethodError`. (Maxim Krizhanovsky, #1231) ### 3.7.0 / 2017-10-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0...v3.7.0) Enhancements: * Improve compatibility with `--enable-frozen-string-literal` option on Ruby 2.3+. (Pat Allan, #1165) Bug Fixes: * Fix `hash_including` and `hash_excluding` so that they work against subclasses of `Hash`. (Aaron Rosenberg, #1167) ### 3.6.0 / 2017-05-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta2...v3.6.0) Bug Fixes: * Fix "instance variable @color not initialized" warning when using rspec-mocks without rspec-core. (Myron Marston, #1142) * Restore aliased module methods properly when stubbing on 1.8.7. (Samuel Giddins, #1144) * Allow a message chain expectation to be constrained by argument(s). (Jon Rowe, #1156) ### 3.6.0.beta2 / 2016-12-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.6.0.beta1...v3.6.0.beta2) Enhancements: * Add new `without_partial_double_verification { }` API that lets you temporarily turn off partial double verification for an example. (Jon Rowe, #1104) ### 3.6.0.beta1 / 2016-10-09 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0...v3.6.0.beta1) Bug Fixes: * Return the test double instance form `#freeze` (Alessandro Berardi, #1109) * Allow the special logic for stubbing `new` to work when `.method` has been redefined. (Proby, #1119) ### 3.5.0 / 2016-07-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta4...v3.5.0) Enhancements: * Provides a nice string representation of `RSpec::Mocks::MessageExpectation` (Myron Marston, #1095) ### 3.5.0.beta4 / 2016-06-05 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta3...v3.5.0.beta4) Enhancements: * Add `and_throw` to any instance handling. (Tobias Bühlmann, #1068) ### 3.5.0.beta3 / 2016-04-02 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta2...v3.5.0.beta3) Enhancements: * Issue warning when attempting to use unsupported `allow(...).to receive(...).ordered`. (Jon Rowe, #1000) * Add `rspec/mocks/minitest_integration`, to properly integrate rspec-mocks with minitest. (Myron Marston, #1065) ### 3.5.0.beta2 / 2016-03-10 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.5.0.beta1...v3.5.0.beta2) Enhancements: * Improve error message displayed when using `and_wrap_original` on pure test doubles. (betesh, #1063) Bug Fixes: * Fix issue that prevented `receive_message_chain(...).with(...)` working correctly on "any instance" mocks. (Jon Rowe, #1061) ### 3.5.0.beta1 / 2016-02-06 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.1...v3.5.0.beta1) Bug Fixes: * Allow `any_instance_of(...).to receive(...)` to use `and_yield` multiple times. (Kilian Cirera Sant, #1054) * Allow matchers which inherit from `rspec-mocks` matchers to be used for `allow`. (Andrew Kozin, #1056) * Prevent stubbing `respond_to?` on partial doubles from causing infinite recursion. (Jon Rowe, #1013) * Prevent aliased methods from disapearing after being mocked with `any_instance` (regression from #1043). (Joe Rafaniello, #1060) ### 3.4.1 / 2016-01-10 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.4.0...v3.4.1) Bug Fixes: * Fix `any_instance` to work properly on Ruby 2.3. (Joe Rafaniello, #1043) ### 3.4.0 / 2015-11-11 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.2...v3.4.0) Enhancements: * Make `expect(...).to have_received` work without relying upon rspec-expectations. (Myron Marston, #978) * Add option for failing tests when expectations are set on `nil`. (Liz Rush, #983) Bug Fixes: * Fix `have_received { ... }` so that any block passed when the message was received is forwarded to the `have_received` block. (Myron Marston, #1006) * Fix infinite loop in error generator when stubbing `respond_to?`. (Alex Dowad, #1022) * Fix issue with using `receive` on subclasses (at a class level) with 1.8.7. (Alex Dowad, #1026) ### 3.3.2 / 2015-07-15 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.1...v3.3.2) Bug Fixes: * Prevent thread deadlock errors during proxy creation (e.g. when using `before_verifying_doubles` callbacks). (Jon Rowe, #980, #979) ### 3.3.1 / 2015-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.3.0...v3.3.1) Bug Fixes: * Fix bug in `before_verifying_double` callback logic that caused it to be called once for each class in the ancestor list when mocking or stubbing a class. Now it is only called for the mocked or stubbed class, as you would expect. (Sam Phippen, #974) ### 3.3.0 / 2015-06-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.1...v3.3.0) Enhancements: * When stubbing `new` on `MyClass` or `class_double(MyClass)`, use the method signature from `MyClass#initialize` to verify arguments. (Myron Marston, #886) * Use matcher descriptions when generating description of received arguments for mock expectation failures. (Tim Wade, #891) * Avoid loading `stringio` unnecessarily. (Myron Marston, #894) * Verifying doubles failure messages now distinguish between class and instance level methods. (Tim Wade, #896, #908) * Improve mock expectation failure messages so that it combines both number of times and the received arguments in the output. (John Ceh, #918) * Improve how test doubles are represented in failure messages. (Siva Gollapalli, Myron Marston, #932) * Rename `RSpec::Mocks::Configuration#when_declaring_verifying_double` to `RSpec::Mocks::Configuration#before_verifying_doubles` and utilise when verifying partial doubles. (Jon Rowe, #940) * Use rspec-support's `ObjectFormatter` for improved formatting of arguments in failure messages so that, for example, full time precisions is displayed for time objects. (Gavin Miller, Myron Marston, #955) Bug Fixes: * Ensure expectations that raise eagerly also raise during RSpec verification. This means that if exceptions are caught inside test execution the test will still fail. (Sam Phippen, #884) * Fix `have_received(msg).with(args).exactly(n).times` and `receive(msg).with(args).exactly(n).times` failure messages for when the message was received the wrong number of times with the specified args, and also received additional times with other arguments. Previously it confusingly listed the arguments as being mis-matched (even when the double was allowed to receive with any args) rather than listing the count. (John Ceh, #918) * Fix `any_args`/`anything` support so that we avoid calling `obj == anything` on user objects that may have improperly implemented `==` in a way that raises errors. (Myron Marston, #924) * Fix edge case involving stubbing the same method on a class and a subclass which previously hit a `NoMethodError` internally in RSpec. (Myron Marston #954) * Fix edge case where the message received count would be incremented multiple times for one failure. (Myron Marston, #957) * Fix failure messages for when spies received the expected message with different arguments and also received another message. (Maurício Linhares, #960) * Silence whitespace-only diffs. (Myron Marston, #969) ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) Bug Fixes: * Add missing `rspec/support/differ` require so that rspec-mocks can be used w/o rspec-expectations (which also loads the differ and hided the fact we forgot to require it). (Myron Marston, #893) * Revert tracking of received arg mutation (added in 3.2.0 to provide an error in a situation we can't support) as our implementation has side effects on non-standard objects and there's no solution we could come up with that always works. (Myron Marston, #900) ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.3...v3.2.0) Enhancements: * Treat `any_args` as an arg splat, allowing it to match an arbitrary number of args at any point in an arg list. (Myron Marston, #786) * Print diffs when arguments in mock expectations are mismatched. (Sam Phippen, #751) * Support names for verified doubles (`instance_double`, `instance_spy`, `class_double`, `class_spy`, `object_double`, `object_spy`). (Cezary Baginski, #826) * Make `array_including` and `hash_including` argument matchers composable. (Sam Phippen, #819) * Make `allow_any_instance_of(...).to receive(...).and_wrap_original` work. (Ryan Fitzgerald, #869) Bug Fixes: * Provide a clear error when users wrongly combine `no_args` with additional arguments (e.g. `expect().to receive().with(no_args, 1)`). (Myron Marston, #786) * Provide a clear error when users wrongly use `any_args` multiple times in the same argument list (e.g. `expect().to receive().with(any_args, 1, any_args)`. (Myron Marston, #786) * Prevent the error generator from using user object #description methods. See [#685](https://github.com/rspec/rspec-mocks/issues/685). (Sam Phippen, #751) * Make verified doubles declared as `(instance|class)_double(SomeConst)` work properly when `SomeConst` has previously been stubbed. `(instance|class)_double("SomeClass")` already worked properly. (Myron Marston, #824) * Add a matcher description for `receive`, `receive_messages` and `receive_message_chain`. (Myron Marston, #828) * Validate invocation args for null object verified doubles. (Myron Marston, #829) * Fix `RSpec::Mocks::Constant.original` when called with an invalid constant to return an object indicating the constant name is invalid, rather than blowing up. (Myron Marston, #833) * Make `extend RSpec::Mocks::ExampleMethods` on any object work properly to add the rspec-mocks API to that object. Previously, `expect` would be undefined. (Myron Marston, #846) * Fix `require 'rspec/mocks/standalone'` so that it only affects `main` and not every object. It's really only intended to be used in a REPL like IRB, but some gems have loaded it, thinking it needs to be loaded when using rspec-mocks outside the context of rspec-core. (Myron Marston, #846) * Prevent message expectations from being modified by customization methods (e.g. `with`) after they have been invoked. (Sam Phippen and Melanie Gilman, #837) * Handle cases where a method stub cannot be removed due to something external to RSpec monkeying with the method definition. This can happen, for example, when you `file.reopen(io)` after previously stubbing a method on the `file` object. (Myron Marston, #853) * Provide a clear error when received message args are mutated before a `have_received(...).with(...)` expectation. (Myron Marston, #868) ### 3.1.3 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.2...v3.1.3) Bug Fixes: * Correct received messages count when used with `have_received` matcher. (Jon Rowe, #793) * Provide a clear error message when you use `allow_any_instance_of(...)` or `expect_any_instance_of(...)` with the `have_received` matcher (they are not intended to be used together and previously caused an odd internal failure in rspec-mocks). (Jon Rowe, #799). * Fix verified double `with` verification so that it applies to method stubs. (Myron Marston, #790) ### 3.1.2 / 2014-09-26 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.1...v3.1.2) Bug Fixes: * Provide a clear error message when you use `allow(...)` with the `have_received` matcher (they are not intended to be used together and previously caused an odd internal failure in rspec-mocks). (Jon Rowe, #788). ### 3.1.1 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.1.0...v3.1.1) Bug Fixes: * Prevent included modules being detected as prepended modules on Ruby 2.0 when using `any_instance_of(...)`. (Tony Novak, #781) ### 3.1.0 / 2014-09-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.4...v3.1.0) Enhancements: * Add spying methods (`spy`, `ìnstance_spy`, `class_spy` and `object_spy`) which create doubles as null objects for use with spying in testing. (Sam Phippen, #671) * `have_received` matcher will raise "does not implement" errors correctly when used with verifying doubles and partial doubles. (Xavier Shay, #722) * Allow matchers to be used in place of keyword arguments in `with` expectations. (Xavier Shay, #726) * Add `thrice` modifier to message expectation interface as a synonym for `exactly(3).times`. (Dennis Taylor, #753) * Add more `thrice` synonyms e.g. `.at_least(:thrice)`, `.at_most(:thrice)`, `receive(...).thrice` and `have_received(...).thrice`. (Jon Rowe, #754) * Add `and_wrap_original` modifier for partial doubles to mutate the response from a method. (Jon Rowe, #762) Bug Fixes: * Remove `any_number_of_times` from `any_instance` recorders that were erroneously causing mention of the method in documentation. (Jon Rowe, #760) * Prevent included modules being detected as prepended modules on Ruby 2.0. (Eugene Kenny, #771) ### 3.0.4 / 2014-08-14 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.3...v3.0.4) Bug Fixes: * Restore `kind_of(x)` to match using `arg.kind_of?(x)` (like RSpec 2) rather than `x === arg`. (Jon Rowe, #750) ### 3.0.3 / 2014-07-21 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.2...v3.0.3) Bug Fixes: * `have_received` matcher will raise "does not implement" errors correctly when used with verifying doubles and partial doubles. (Xavier Shay, #722) * Make `double.as_null_object.dup` and `double.as_null_object.clone` make the copies be null objects. (Myron Marston, #732) * Don't inadvertently define `BasicObject` in 1.8.7. (Chris Griego, #739) ### 3.0.2 / 2014-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.1...v3.0.2) Bug Fixes: * Fix edge case that triggered "can't add a new key into hash during iteration" during mock verification. (Sam Phippen, Myron Marston, #711) * Fix verifying doubles so that when they accidentally leak into another example, they provide the same clear error message that normal doubles do. (Myron Marston, #718) * Make `ordered` work with exact receive counts. (Sam Phippen, #713) ### 3.0.1 / 2014-06-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0...v3.0.1) Bug Fixes: * Fix `receive_message_chain(...)` so that it supports `with` just like `stub_chain` did. (Jon Rowe, #697) * Fix regression in `expect_any_instance_of` so that it expects the message on _any_ instance rather than on _every_ instance. (Myron Marston, #699) ### 3.0.0 / 2014-06-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.rc1...v3.0.0) Bug Fixes: * Fix module prepend detection to work properly on ruby 2.0 for a case where a module is extended onto itself. (Myron Marston) * Fix `transfer_nested_constants` option so that transferred constants get properly reset at the end of the example. (Myron Marston) * Fix `config.transfer_nested_constants = true` so that you don't erroneously get errors when stubbing a constant that is not a module or a class. (Myron Marston) * Fix regression that caused `double(:class => SomeClass)` to later trigger infinite recursion. (Myron Marston) * Fix bug in `have_received(...).with(...).ordered` where it was not taking the args into account when checking the order. (Myron Marston) * Fix bug in `have_received(...).ordered` where it was wrongly considering stubs when checking the order. (Myron Marston) * Message expectation matchers now show descriptions from argument matchers when their expectations aren't met. (Jon Rowe) * Display warning when encountering `TypeError` during instance method staging on 2.0.0-p195, suffers from https://bugs.ruby-lang.org/issues/8686 too. (Cezar Halmagean). ### 3.0.0.rc1 / 2014-05-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta2...v3.0.0.rc1) Breaking Changes for 3.0.0: * Remove `RSpec::Mocks::TestDouble.extend_onto`. (Myron Marston) * Remove `RSpec::Mocks::ConstantStubber`. (Jon Rowe) * Make monkey-patch of Marshal to support dumping of stubbed objects opt-in. (Xavier Shay) Enhancements: * Instead of crashing when cleaning up stub methods on a frozen object, it now issues a warning explaining that it's impossible to clean up the stubs. (Justin Coyne and Sam Phippen) * Add meaningful descriptions to `anything`, `duck_type` and `instance_of` argument matchers. (Jon Rowe) Bug Fixes: * Fix regression introduced in 3.0.0.beta2 that caused `double.as_null_object.to_str` to return the double rather than a string. (Myron Marston) * Fix bug in `expect(dbl).to receive_message_chain(:foo, :bar)` where it was not setting an expectation for the last message in the chain. (Jonathan del Strother) * Allow verifying partial doubles to have private methods stubbed. (Xavier Shay) * Fix bug with allowing/expecting messages on Class objects which have had their singleton class prepended to. (Jon Rowe) * Fix an issue with 1.8.7 not running implementation blocks on partial doubles. (Maurício Linhares) * Prevent `StackLevelTooDeep` errors when stubbing an `any_instance` method that's accessed in `inspect` by providing our own inspect output. (Jon Rowe) * Fix bug in `any_instance` logic that did not allow you to mock or stub private methods if `verify_partial_doubles` was configured. (Oren Dobzinski) * Include useful error message when trying to observe an unimplemented method on an any instance. (Xavier Shay) * Fix `and_call_original` to work properly when multiple classes in an inheritance hierarchy have been stubbed with the same method. (Myron Marston) * Fix `any_instance` so that it updates existing instances that have already been stubbed. (Myron Marston) * Fix verified doubles so that their class name is included in failure messages. (Myron Marston) * Fix `expect_any_instance_of` so that when the message is received on an individual instance that has been directly stubbed, it still satisfies the expectation. (Sam Phippen, Myron Marston) * Explicitly disallow using `any_instance` to mock or stub a method that is defined on a module prepended onto the class. This triggered `SystemStackError` before and is very hard to support so we are not supporting it at this time. (Myron Marston) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.0.0.beta1...v3.0.0.beta2) Breaking Changes for 3.0.0: * Rename `RSpec::Mocks::Mock` to `RSpec::Mocks::Double`. (Myron Marston) * Change how to integrate rspec-mocks in other test frameworks. You now need to include `RSpec::Mocks::ExampleMethods` in your test context. (Myron Marston) * Prevent RSpec mocks' doubles and partial doubles from being used outside of the per-test lifecycle (e.g. from a `before(:all)` hook). (Sam Phippen) * Remove the `host` argument of `RSpec::Mocks.setup`. Instead `RSpec::Mocks::ExampleMethods` should be included directly in the scope where RSpec's mocking capabilities are used. (Sam Phippen) * Make test doubles raise errors if you attempt to use them after they get reset, to help surface issues when you accidentally retain references to test doubles and attempt to reuse them in another example. (Myron Marston) * Remove support for `and_return { value }` and `and_return` without arguments. (Yuji Nakayama) Enhancements: * Add `receive_message_chain` which provides the functionality of the old `stub_chain` for the new allow/expect syntax. Use it like so: `allow(...).to receive_message_chain(:foo, :bar, :bazz)`. (Sam Phippen). * Change argument matchers to use `===` as their primary matching protocol, since their semantics mirror that of a case or rescue statement (which uses `===` for matching). (Myron Marston) * Add `RSpec::Mocks.with_temporary_scope`, which allows you to create temporary rspec-mocks scopes in arbitrary places (such as a `before(:all)` hook). (Myron Marston) * Support keyword arguments when checking arity with verifying doubles. (Xavier Shay) Bug Fixes: * Fix regression in 3.0.0.beta1 that caused `double("string_name" => :value)` to stop working. (Xavier Shay) * Fix the way rspec-mocks and rspec-core interact so that if users define a `let` with the same name as one of the methods from `RSpec::Mocks::ArgumentMatchers`, the user's `let` takes precedence. (Michi Huber, Myron Marston) * Fix verified doubles so that their methods match the visibility (public, protected or private) of the interface they verify against. (Myron Marston) * Fix verified null object doubles so that they do not wrongly report that they respond to anything. They only respond to methods available on the interface they verify against. (Myron Marston) * Fix deprecation warning for use of old `:should` syntax w/o explicit config so that it no longer is silenced by an extension gem such as rspec-rails when it calls `config.add_stub_and_should_receive_to`. (Sam Phippen) * Fix `expect` syntax so that it does not wrongly emit a "You're overriding a previous implementation for this stub" warning when you are not actually doing that. (Myron Marston) * Fix `any_instance.unstub` when used on sub classes for whom the super class has had `any_instance.stub` invoked on. (Jon Rowe) * Fix regression in `stub_chain`/`receive_message_chain` that caused it to raise an `ArgumentError` when passing args to the stubbed methods. (Sam Phippen) * Correct stub of undefined parent modules all the way down when stubbing a nested constant. (Xavier Shay) * Raise `VerifyingDoubleNotDefinedError` when a constant is not defined for a verifying class double. (Maurício Linhares) * Remove `Double#to_str`, which caused confusing `raise some_double` behavior. (Maurício Linhares) ### 3.0.0.beta1 / 2013-11-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.4...v3.0.0.beta1) Breaking Changes for 3.0.0: * Raise an explicit error if `should_not_receive(...).and_return` is used. (Sam Phippen) * Remove 1.8.6 workarounds. (Jon Rowe) * Remove `stub!` and `unstub!`. (Sam Phippen) * Remove `mock(name, methods)` and `stub(name, methods)`, leaving `double(name, methods)` for creating test doubles. (Sam Phippen, Michi Huber) * Remove `any_number_of_times` since `should_receive(:msg).any_number_of_times` is really a stub in a mock's clothing. (Sam Phippen) * Remove support for re-using the same null-object test double in multiple examples. Test doubles are designed to only live for one example. (Myron Marston) * Make `at_least(0)` raise an error. (Sam Phippen) * Remove support for `require 'spec/mocks'` which had been kept in place for backwards compatibility with RSpec 1. (Myron Marston) * Blocks provided to `with` are always used as implementation. (Xavier Shay) * The config option (added in 2.99) to yield the receiver to `any_instance` implementation blocks now defaults to "on". (Sam Phippen) Enhancements: * Allow the `have_received` matcher to use a block to set further expectations on arguments. (Tim Cowlishaw) * Provide `instance_double` and `class_double` to create verifying doubles, ported from `rspec-fire`. (Xavier Shay) * `as_null_object` on a verifying double only responds to defined methods. (Xavier Shay) * Provide `object_double` to create verified doubles of specific object instances. (Xavier Shay) * Provide `verify_partial_doubles` configuration that provides `object_double` like verification behaviour on partial doubles. (Xavier Shay) * Improved performance of double creation, particularly those with many attributes. (Xavier Shay) * Default value of `transfer_nested_constants` option for constant stubbing can be configured. (Xavier Shay) * Messages can be allowed or expected on in bulk via `receive_messages(:message => :value)`. (Jon Rowe) * `allow(Klass.any_instance)` and `expect(Klass.any_instance)` now print a warning. This is usually a mistake, and users usually want `allow_any_instance_of` or `expect_any_instance_of` instead. (Sam Phippen) * `instance_double` and `class_double` raise `ArgumentError` if the underlying module is loaded and the arity of the method being invoked does not match the arity of the method as it is actually implemented. (Andy Lindeman) * Spies can now check their invocation ordering is correct. (Jon Rowe) Deprecations: * Using the old `:should` syntax without explicitly configuring it is deprecated. It will continue to work but will emit a deprecation warning in RSpec 3 if you do not explicitly enable it. (Sam Phippen) Bug Fixes: * Fix `and_call_original` to handle a complex edge case involving singleton class ancestors. (Marc-André Lafortune, Myron Marston) * When generating an error message for unexpected arguments, use `#inspect` rather than `#description` if `#description` returns `nil` or `''` so that you still get a useful message. (Nick DeLuca) ### 2.99.4 / 2015-06-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.3...v2.99.4) Bug Fixes: * Add missing deprecation for using `with` with no arguments e.g. `with()`. (Yousuke, #970) ### 2.99.3 / 2015-01-09 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.2...v2.99.3) Bug Fixes: * Fix regression that caused an error when a test double was deserialized from YAML. (Yuji Nakayama, #777) ### 2.99.2 / 2014-07-21 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.1...v2.99.2) Enhancements: * Warn about upcoming change to `#===` matching and `DateTime#===` behaviour. (Jon Rowe, #735) ### 2.99.1 / 2014-06-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0...v2.99.1) Bug Fixes: * Fix bug that caused errors at the end of each example when a `double.as_null_object` had been frozen. (Yuji Nakayama, #698) Deprecations: * Deprecate freezing a test double. (Yuji Nakayama, #698) ### 2.99.0 / 2014-06-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.rc1...v2.99.0) No changes. Just taking it out of pre-release. ### 2.99.0.rc1 / 2014-05-18 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta2...v2.99.0.rc1) Deprecations: * Deprecate `RSpec::Mocks::TestDouble.extend_onto`. (Myron Marston) * Deprecate `RSpec::Mocks::ConstantStubber`. (Jon Rowe) * Deprecate `Marshal.dump` monkey-patch without opt-in. (Xavier Shay) ### 2.99.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.99.0.beta1...v2.99.0.beta2) Deprecations: * Deprecate `RSpec::Mocks::Mock` in favor of `RSpec::Mocks::Double`. (Myron Marston) * Deprecate the `host` argument of `RSpec::Mocks.setup`. Instead `RSpec::Mocks::ExampleMethods` should be included directly in the scope where RSpec's mocking capabilities are used. (Sam Phippen) * Deprecate using any of rspec-mocks' features outside the per-test lifecycle (e.g. from a `before(:all)` hook). (Myron Marston) * Deprecate re-using a test double in another example. (Myron Marston) * Deprecate `and_return { value }` and `and_return` without arguments. (Yuji Nakayama) ### 2.99.0.beta1 / 2013-11-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.99.0.beta1) Deprecations * Expecting to use lambdas or other strong arity implementations for stub methods with mis-matched arity is deprecated and support for them will be removed in 3.0. Either provide the right amount of arguments or use a weak arity implementation (methods with splats or procs). (Jon Rowe) * Using the same test double instance in multiple examples is deprecated. Test doubles are only meant to live for one example. The mocks and stubs have always been reset between examples; however, in 2.x the `as_null_object` state was not reset and some users relied on this to have a null object double that is used for many examples. This behavior will be removed in 3.0. (Myron Marston) * Print a detailed warning when an `any_instance` implementation block is used when the new `yield_receiver_to_any_instance_implementation_blocks` config option is not explicitly set, as RSpec 3.0 will default to enabling this new feature. (Sam Phippen) Enhancements: * Add a config option to yield the receiver to `any_instance` implementation blocks. (Sam Phippen) ### 2.14.6 / 2014-02-20 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.5...v2.14.6) Bug Fixes: * Ensure `any_instance` method stubs and expectations are torn down regardless of expectation failures. (Sam Phippen) ### 2.14.5 / 2014-02-01 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.14.5) Bug Fixes: * Fix regression that caused block implementations to not receive all args on 1.8.7 if the block also receives a block, due to Proc#arity reporting `1` no matter how many args the block receives if it receives a block, too. (Myron Marston) ### 2.14.4 / 2013-10-15 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.3...v2.14.4) Bug Fixes: * Fix issue where unstubing methods on "any instances" would not remove stubs on existing instances (Jon Rowe) * Fix issue with receive(:message) do ... end precedence preventing the usage of modifications (`and_return` etc) (Jon Rowe) ### 2.14.3 / 2013-08-08 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.2...v2.14.3) Bug Fixes: * Fix stubbing some instance methods for classes whose hierarchy includes a prepended Module (Bradley Schaefer) ### 2.14.2 / 2013-07-30 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.1...v2.14.2) Bug Fixes: * Fix `as_null_object` doubles so that they return `nil` from `to_ary` (Jon Rowe). * Fix regression in 2.14 that made `stub!` (with an implicit receiver) return a test double rather than stub a method (Myron Marston). ### 2.14.1 / 2013-07-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0...v2.14.1) Bug Fixes: * Restore `double.as_null_object` behavior from 2.13 and earlier: a double's nullness persisted between examples in earlier examples. While this is not an intended use case (test doubles are meant to live for only one example), we don't want to break behavior users rely on in a minor relase. This will be deprecated in 2.99 and removed in 3.0. (Myron Marston) ### 2.14.0 / 2013-07-06 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.0.rc1...v2.14.0) Enhancements: * Document test spies in the readme. (Adarsh Pandit) * Add an `array_including` matcher. (Sam Phippen) * Add a syntax-agnostic API for mocking or stubbing a method. This is intended for use by libraries such as rspec-rails that need to mock or stub a method, and work regardless of the syntax the user has configured (Paul Annesley, Myron Marston and Sam Phippen). Bug Fixes: * Fix `double` so that it sets up passed stubs correctly regardless of the configured syntax (Paul Annesley). * Allow a block implementation to be used in combination with `and_yield`, `and_raise`, `and_return` or `and_throw`. This got fixed in 2.13.1 but failed to get merged into master for the 2.14.0.rc1 release (Myron Marston). * `Marshal.dump` does not unnecessarily duplicate objects when rspec-mocks has not been fully initialized. This could cause errors when using `spork` or similar preloading gems (Andy Lindeman). ### 2.14.0.rc1 / 2013-05-27 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.14.0.rc1) Enhancements: * Refactor internals so that the mock proxy methods and state are held outside of the mocked object rather than inside it. This paves the way for future syntax enhancements and removes the need for some hacky work arounds for `any_instance` dup'ing and `YAML` serialization, among other things. Note that the code now relies upon `__id__` returning a unique, consistent value for any object you want to mock or stub (Myron Marston). * Add support for test spies. This allows you to verify a message was received afterwards using the `have_received` matcher. Note that you must first stub the method or use a null double. (Joe Ferris and Joël Quenneville) * Make `at_least` and `at_most` style receive expectations print that they were expecting at least or at most some number of calls, rather than just the number of calls given in the expectation (Sam Phippen) * Make `with` style receive expectations print the args they were expecting, and the args that they got (Sam Phippen) * Fix some warnings seen under ruby 2.0.0p0 (Sam Phippen). * Add a new `:expect` syntax for message expectations (Myron Marston and Sam Phippen). Bug fixes * Fix `any_instance` so that a frozen object can be `dup`'d when methods have been stubbed on that type using `any_instance` (Jon Rowe). * Fix `and_call_original` so that it properly raises an `ArgumentError` when the wrong number of args are passed (Jon Rowe). * Fix `double` on 1.9.2 so you can wrap them in an Array using `Array(my_double)` (Jon Rowe). * Fix `stub_const` and `hide_const` to handle constants that redefine `send` (Sam Phippen). * Fix `Marshal.dump` extension so that it correctly handles nil. (Luke Imhoff, Jon Rowe) * Fix isolation of `allow_message_expectations_on_nil` (Jon Rowe) * Use inspect to format actual arguments on expectations in failure messages (#280, Ben Langfeld) * Protect against improperly initialised test doubles (#293) (Joseph Shraibman and Jon Rowe) Deprecations * Deprecate `stub` and `mock` as aliases for `double`. `double` is the best term for creating a test double, and it reduces confusion to have only one term (Michi Huber). * Deprecate `stub!` and `unstub!` in favor of `stub` and `unstub` (Jon Rowe). * Deprecate `at_least(0).times` and `any_number_of_times` (Michi Huber). ### 2.13.1 / 2013-04-06 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.13.0...v2.13.1) Bug fixes * Allow a block implementation to be used in combination with `and_yield`, `and_raise`, `and_return` or `and_throw` (Myron Marston). ### 2.13.0 / 2013-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.2...v2.13.0) Bug fixes * Fix bug that caused weird behavior when a method that had previously been stubbed with multiple return values (e.g. `obj.stub(:foo).and_return(1, 2)`) was later mocked with a single return value (e.g. `obj.should_receive(:foo).once.and_return(1)`). (Myron Marston) * Fix bug related to a mock expectation for a method that already had multiple stubs with different `with` constraints. Previously, the first stub was used, even though it may not have matched the passed args. The fix defers this decision until the message is received so that the proper stub response can be chosen based on the passed arguments (Myron Marston). * Do not call `nil?` extra times on a mocked object, in case `nil?` itself is expected a set number of times (Myron Marston). * Fix `missing_default_stub_error` message so array args are handled properly (Myron Marston). * Explicitly disallow `any_instance.unstub!` (Ryan Jones). * Fix `any_instance` stubbing so that it works with `Delegator` subclasses (Myron Marston). * Fix `and_call_original` so that it works with `Delegator` subclasses (Myron Marston). * Fix `any_instance.should_not_receive` when `any_instance.should_receive` is used on the same class in the same example. Previously it would wrongly report a failure even when the message was not received (Myron Marston). ### 2.12.2 / 2013-01-27 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.1...v.2.12.2) Bug fixes * Fix `and_call_original` to work properly for methods defined on a module extended onto an object instance (Myron Marston). * Fix `stub_const` with an undefined constnat name to work properly with constant strings that are prefixed with `::` -- and edge case I missed in the bug fix in the 2.12.1 release (Myron Marston). * Ensure method visibility on a partial mock is restored after reseting method stubs, even on a singleton module (created via `extend self`) when the method visibility differs between the instance and singleton versions (Andy Lindeman). ### 2.12.1 / 2012-12-21 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.0...v2.12.1) Bug fixes * Fix `any_instance` to support `and_call_original`. (Myron Marston) * Properly restore stubbed aliased methods on rubies that report the incorrect owner (Myron Marston and Andy Lindeman). * Fix `hide_const` and `stub_const` with a defined constnat name to work properly with constant strings that are prefixed with `::` (Myron Marston). ### 2.12.0 / 2012-11-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.3...v2.12.0) Enhancements * `and_raise` can accept an exception class and message, more closely matching `Kernel#raise` (e.g., `foo.stub(:bar).and_raise(RuntimeError, "message")`) (Bas Vodde) * Add `and_call_original`, which will delegate the message to the original method (Myron Marston). Deprecations: * Add deprecation warning when using `and_return` with `should_not_receive` (Neha Kumari) ### 2.11.3 / 2012-09-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.2...v2.11.3) Bug fixes * Fix `:transfer_nested_constants` option of `stub_const` so that it doesn't blow up when there are inherited constants. (Myron Marston) * `any_instance` stubs can be used on classes that override `Object#method`. (Andy Lindeman) * Methods stubbed with `any_instance` are unstubbed after the test finishes. (Andy Lindeman) * Fix confusing error message when calling a mocked class method an extra time with the wrong arguments (Myron Marston). ### 2.11.2 / 2012-08-11 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.1...v2.11.2) Bug fixes * Don't modify `dup` on classes that don't support `dup` (David Chelimsky) * Fix `any_instance` so that it works properly with methods defined on a superclass. (Daniel Eguzkiza) * Fix `stub_const` so that it works properly for nested constants that share a name with a top-level constant (e.g. "MyGem::Hash"). (Myron Marston) ### 2.11.1 / 2012-07-09 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.0...v2.11.1) Bug fixes * Fix `should_receive` so that when it is called on an `as_null_object` double with no implementation, and there is a previous explicit stub for the same method, the explicit stub remains (rather than being overridden with the null object implementation--`return self`). (Myron Marston) ### 2.11.0 / 2012-07-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.10.1...v2.11.0) Enhancements * Expose ArgumentListMatcher as a formal API * supports use by 3rd party mock frameworks like Surrogate * Add `stub_const` API to stub constants for the duration of an example (Myron Marston). Bug fixes * Fix regression of edge case behavior. `double.should_receive(:foo) { a }` was causing a NoMethodError when `double.stub(:foo).and_return(a, b)` had been setup before (Myron Marston). * Infinite loop generated by using `any_instance` and `dup`. (Sidu Ponnappa @kaiwren) * `double.should_receive(:foo).at_least(:once).and_return(a)` always returns a even if `:foo` is already stubbed. * Prevent infinite loop when interpolating a null double into a string as an integer (`"%i" % double.as_null_object`). (Myron Marston) * Fix `should_receive` so that null object behavior (e.g. returning self) is preserved if no implementation is given (Myron Marston). * Fix `and_raise` so that it raises `RuntimeError` rather than `Exception` by default, just like ruby does. (Andrew Marshall) ### 2.10.1 / 2012-05-05 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.10.0...v2.10.1) Bug fixes * fix regression of edge case behavior (https://github.com/rspec/rspec-mocks/issues/132) * fixed failure of `object.should_receive(:message).at_least(0).times.and_return value` * fixed failure of `object.should_not_receive(:message).and_return value` ### 2.10.0 / 2012-05-03 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.9.0...v2.10.0) Bug fixes * fail fast when an `exactly` or `at_most` expectation is exceeded ### 2.9.0 / 2012-03-17 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0...v2.9.0) Enhancements * Support order constraints across objects (preethiramdev) Bug fixes * Allow a `as_null_object` to be passed to `with` * Pass proc to block passed to stub (Aubrey Rhodes) * Initialize child message expectation args to match any args (#109 - preethiramdev) ### 2.8.0 / 2012-01-04 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0.rc2...v2.8.0) No changes for this release. Just releasing with the other rspec gems. ### 2.8.0.rc2 / 2011-12-19 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0.rc1...v2.8.0.rc2) No changes for this release. Just releasing with the other rspec gems. ### 2.8.0.rc1 / 2011-11-06 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.7.0...v2.8.0.rc1) Enhancements * Eliminate Ruby warnings (Matijs van Zuijlen) ### 2.7.0 / 2011-10-16 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.6.0...v2.7.0) Enhancements * Use `__send__` rather than `send` (alextk) * Add support for `any_instance.stub_chain` (Sidu Ponnappa) * Add support for `any_instance` argument matching based on `with` (Sidu Ponnappa and Andy Lindeman) Changes * Check for `failure_message_for_should` or `failure_message` instead of `description` to detect a matcher (Tibor Claassen) Bug fixes * pass a hash to `any_instance.stub`. (Justin Ko) * allow `to_ary` to be called without raising `NoMethodError` (Mikhail Dieterle) * `any_instance` properly restores private methods (Sidu Ponnappa) ### 2.6.0 / 2011-05-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.5.0...v2.6.0) Enhancements * Add support for `any_instance.stub` and `any_instance.should_receive` (Sidu Ponnappa and Andy Lindeman) Bug fixes * fix bug in which multiple chains with shared messages ending in hashes failed to return the correct value ### 2.5.0 / 2011-02-05 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.4.0...v2.5.0) Bug fixes * message expectation counts now work in combination with a stub (Damian Nurzynski) * fix failure message when message received with incorrect args (Josep M. Bach) ### 2.4.0 / 2011-01-02 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.3.0...v2.4.0) No functional changes in this release, which was made to align with the rspec-core-2.4.0 release. ### 2.3.0 / 2010-12-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.2.0...v2.3.0) Bug fixes * Fix our Marshal extension so that it does not interfere with objects that have their own `@mock_proxy` instance variable. (Myron Marston) ### 2.2.0 / 2010-11-28 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.1.0...v2.2.0) Enhancements * Added "rspec/mocks/standalone" for exploring the rspec-mocks in irb. Bug fix * Eliminate warning on splat args without parens (Gioele Barabucci) * Fix bug where `obj.should_receive(:foo).with(stub.as_null_object)` would pass with a false positive. ### 2.1.0 / 2010-11-07 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.1...v2.1.0) Bug fixes * Fix serialization of stubbed object (Josep M Bach) ### 2.0.0 / 2010-10-10 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.0.beta.22...v2.0.0) ### 2.0.0.rc / 2010-10-05 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.0.beta.22...v2.0.0.rc) Enhancements * support passing a block to an expectation block (Nicolas Braem) * `obj.should_receive(:msg) {|&block| ... }` Bug fixes * Fix YAML serialization of stub (Myron Marston) * Fix rdoc rake task (Hans de Graaff) ### 2.0.0.beta.22 / 2010-09-12 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v2.0.0.beta.20...v2.0.0.beta.22) Bug fixes * fixed regression that broke `obj.stub_chain(:a, :b => :c)` * fixed regression that broke `obj.stub_chain(:a, :b) { :c }` * `respond_to?` always returns true when using `as_null_object` rspec-mocks-3.13.0/DEV-README.md000066400000000000000000000012561455767030500157370ustar00rootroot00000000000000## Set up the dev environment git clone https://github.com/rspec/rspec-mocks.git cd rspec-mocks gem install bundler bundle install Now you should be able to run any of: rake rake spec rake cucumber Or, if you prefer to use the rspec and cucumber commands directly, you can either: bundle exec rspec Or ... bundle install --binstubs bin/rspec ## Customize the dev environment The Gemfile includes the gems you'll need to be able to run specs. If you want to customize your dev environment with additional tools like guard or ruby-debug, add any additional gem declarations to Gemfile-custom (see Gemfile-custom.sample for some examples). rspec-mocks-3.13.0/DEVELOPMENT.md000066400000000000000000000115161455767030500161500ustar00rootroot00000000000000 # Development Setup Generally speaking, you only need to clone the project and install the dependencies with [Bundler](https://bundler.io/). You can either get a full RSpec development environment using [rspec-dev](https://github.com/rspec/rspec-dev#README) or you can set this project up individually. ## Setting up rspec-mocks individually For most contributors, setting up the project individually will be simpler. Unless you have a specific reason to use rspec-dev, we recommend using this approach. Clone the repo: ``` $ git clone git@github.com:rspec/rspec-mocks.git ``` Install the dependencies using [Bundler](https://bundler.io/): ``` $ cd rspec-mocks $ bundle install ``` To minimize boot time and to ensure we don't depend upon any extra dependencies loaded by Bundler, our CI builds avoid loading Bundler at runtime by using Bundler's [`--standalone option`](https://myronmars.to/n/dev-blog/2012/03/faster-test-boot-times-with-bundler-standalone). While not strictly necessary (many/most of our contributors do not do this!), if you want to exactly reproduce our CI builds you'll want to do the same: ``` $ bundle install --standalone --binstubs ``` The `--binstubs` option creates the `bin/rspec` file that, like `bundle exec rspec`, will load all the versions specified in `Gemfile.lock` without loading bundler at runtime! ## Using rspec-dev See the [rspec-dev README](https://github.com/rspec/rspec-dev#README) for setup instructions. The rspec-dev project contains many rake tasks for helping manage an RSpec development environment, making it easy to do things like: * Change branches across all repos * Update all repos with the latest code from `main` * Cut a new release across all repos * Push out updated build scripts to all repos These sorts of tasks are essential for the RSpec maintainers but will probably be unnecessary complexity if you're just contributing to one repository. If you are getting setup to make your first contribution, we recommend you take the simpler route of setting up rspec-mocks individually. ## Gotcha: Version mismatch from sibling repos The [Gemfile](Gemfile) is designed to be flexible and support using the other RSpec repositories either from a local sibling directory (e.g. `../rspec-`) or, if there is no such directory, directly from git. This generally does the "right thing", but can be a gotcha in some situations. For example, if you are setting up `rspec-core`, and you happen to have an old clone of `rspec-expectations` in a sibling directory, it'll be used even though it might be months or years out of date, which can cause confusing failures. To avoid this problem, you can either `export USE_GIT_REPOS=1` to force the use of `:git` dependencies instead of local dependencies, or update the code in the sibling directory. rspec-dev contains rake tasks to help you keep all repos in sync. ## Extra Gems If you need additional gems for any tasks---such as `benchmark-ips` for benchmarking or `byebug` for debugging---you can create a `Gemfile-custom` file containing those gem declarations. The `Gemfile` evaluates that file if it exists, and it is git-ignored. # Running the build The [Travis CI build](https://travis-ci.org/rspec/rspec-mocks) runs many verification steps to prevent regressions and ensure high-quality code. To run the Travis build locally, run: ``` $ script/run_build ``` See [build detail](BUILD_DETAIL.md) for more detail. # What to Expect To ensure high, uniform code quality, all code changes (including changes from the maintainers!) are subject to a pull request code review. We'll often ask for clarification or suggest alternate ways to do things. Our code reviews are intended to be a two-way conversation. Here's a short, non-exhaustive checklist of things we typically ask contributors to do before PRs are ready to merge. It can help get your PR merged faster if you do these in advance! - [ ] New behavior is covered by tests and all tests are passing. - [ ] No Ruby warnings are issued by your changes. - [ ] Documentation reflects changes and renders as intended. - [ ] RuboCop passes (e.g. `bundle exec rubocop lib`). - [ ] Commits are squashed into a reasonable number of logical changesets that tell an easy-to-follow story. - [ ] No changelog entry is necessary (we'll add it as part of the merge process!) # Adding Docs RSpec uses [YARD](https://yardoc.org/) for its API documentation. To ensure the docs render well, we recommend running a YARD server and viewing your edits in a browser. To run a YARD server: ``` $ bundle exec yard server --reload # or, if you installed your bundle with `--standalone --binstubs`: $ bin/yard server --reload ``` Then navigate to `localhost:8808` to view the rendered docs. rspec-mocks-3.13.0/Gemfile000066400000000000000000000057161455767030500153440ustar00rootroot00000000000000source "https://rubygems.org" gemspec branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp %w[rspec rspec-core rspec-expectations rspec-support].each do |lib| library_path = File.expand_path("../../#{lib}", __FILE__) if File.exist?(library_path) && !ENV['USE_GIT_REPOS'] gem lib, :path => library_path else gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => branch end end if RUBY_VERSION < '1.9.3' gem 'rake', '< 11.0.0' # rake 11 requires Ruby 1.9.3 or later elsif RUBY_VERSION < '2.0.0' gem 'rake', '< 12.0.0' # rake 12 requires Ruby 2.0.0 or later else gem 'rake', '> 12.3.2' end if ENV['DIFF_LCS_VERSION'] gem 'diff-lcs', ENV['DIFF_LCS_VERSION'] else gem 'diff-lcs', '~> 1.4', '>= 1.4.3' end gem 'yard', '~> 0.9.24', :require => false # No need to run rubocop on earlier versions if RUBY_VERSION >= '2.4' && RUBY_ENGINE == 'ruby' gem "rubocop", "~> 1.0", "< 1.12" end # allow gems to be installed on older rubies and/or windows if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) gem 'ffi', '< 1.10' elsif RUBY_VERSION < '2.4.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) gem 'ffi', '< 1.15' elsif RUBY_VERSION < '1.9' gem 'ffi', '< 1.9.19' # ffi dropped Ruby 1.8 support in 1.9.19 elsif RUBY_VERSION < '2.0' gem 'ffi', '< 1.11.0' # ffi dropped Ruby 1.9 support in 1.11.0 elsif RUBY_VERSION < '2.3.0' gem 'ffi', '~> 1.12.0' else gem 'ffi', '~> 1.15.0' end if RUBY_VERSION <= '2.3.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) gem "childprocess", "< 1.0.0" elsif RUBY_VERSION < '2.0.0' gem "childprocess", "< 1.0.0" elsif RUBY_VERSION < '2.3.0' gem "childprocess", "< 3.0.0" else gem "childprocess", ">= 3.0.0" end if RUBY_VERSION < '1.9.2' gem 'contracts', '~> 0.15.0' # is a dependency of aruba end # Version 5.12 of minitest requires Ruby 2.4 if RUBY_VERSION < '2.4.0' gem 'minitest', '< 5.12.0' end if RUBY_VERSION < '2.0.0' gem 'thor', '< 1.0.0' else gem 'thor', '> 1.0.0' end ### deps for rdoc.info group :documentation do gem 'redcarpet', :platform => :mri gem 'github-markup', :platform => :mri end group :coverage do gem 'simplecov', '~> 0.8' end if RUBY_VERSION < '2.0.0' || RUBY_ENGINE == 'java' gem 'json', '< 2.0.0' # this is a dependency of simplecov else gem 'json', '> 2.3.0' end if RUBY_VERSION < '2.0.0' gem 'cucumber', "<= 1.3.22" elsif !ENV['DIFF_LCS_VERSION'].to_s.empty? && ENV['DIFF_LCS_VERSION'].scan(/\d\.\d/).first.to_f < 1.5 # Older version of diff-lcs cause a downstream error with cucumber and modern rails gem "activesupport", "< 7" end platforms :jruby do if RUBY_VERSION < '1.9.0' # Pin jruby-openssl on older J Ruby gem "jruby-openssl", "< 0.10.0" # Pin child-process on older J Ruby gem "childprocess", "< 1.0.0" else gem "jruby-openssl" end end eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') rspec-mocks-3.13.0/Gemfile-custom.sample000066400000000000000000000005111455767030500201200ustar00rootroot00000000000000group :development do gem 'interactive_rspec' gem 'guard-rspec', '~> 1.2.1' gem 'growl', '1.0.3' gem 'spork', '0.9.0' platform :mri do gem 'rb-fsevent', '~> 0.9.0' gem 'ruby-prof', '~> 0.10.0' case RUBY_VERSION when /^1.8/ gem 'ruby-debug' when /^1.9/ gem 'debugger' end end end rspec-mocks-3.13.0/Guardfile000066400000000000000000000004211455767030500156620ustar00rootroot00000000000000# A sample Guardfile # More info at http://github.com/guard/guard#readme guard 'rspec', :version => 2 do watch(/^spec\/(.*)_spec.rb/) watch(/^lib\/(.*)\.rb/) { "spec" } watch(/^spec\/spec_helper.rb/) { "spec" } end rspec-mocks-3.13.0/ISSUE_TEMPLATE.md000066400000000000000000000010271455767030500165450ustar00rootroot00000000000000 ### Subject of the issue ### Your environment * Ruby version: * rspec-mocks version: ### Steps to reproduce ### Expected behavior ### Actual behavior rspec-mocks-3.13.0/LICENSE.md000066400000000000000000000023011455767030500154400ustar00rootroot00000000000000The MIT License (MIT) ===================== * Copyright © 2012 David Chelimsky, Myron Marston * Copyright © 2006 David Chelimsky, The RSpec Development Team * Copyright © 2005 Steven Baker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rspec-mocks-3.13.0/README.md000066400000000000000000000424311455767030500153230ustar00rootroot00000000000000# RSpec Mocks [![Build Status](https://github.com/rspec/rspec-mocks/workflows/RSpec%20CI/badge.svg)](https://github.com/rspec/rspec-mocks/actions) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.svg)](https://codeclimate.com/github/rspec/rspec-mocks) rspec-mocks is a test-double framework for rspec with support for method stubs, fakes, and message expectations on generated test-doubles and real objects alike. ## Install gem install rspec # for rspec-core, rspec-expectations, rspec-mocks gem install rspec-mocks # for rspec-mocks only Want to run against the `main` branch? You'll need to include the dependent RSpec repos as well. Add the following to your `Gemfile`: ```ruby %w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib| gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main' end ``` ## Contributing Once you've set up the environment, you'll need to cd into the working directory of whichever repo you want to work in. From there you can run the specs and cucumber features, and make patches. NOTE: You do not need to use rspec-dev to work on a specific RSpec repo. You can treat each RSpec repo as an independent project. For information about contributing to RSpec, please refer to the following markdown files: * [Build details](BUILD_DETAIL.md) * [Code of Conduct](CODE_OF_CONDUCT.md) * [Detailed contributing guide](CONTRIBUTING.md) * [Development setup guide](DEVELOPMENT.md) ## 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: ```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. ```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. More detail is available in [their documentation](https://github.com/rspec/rspec-mocks/blob/main/features/verifying_doubles). Verifying doubles can also accept custom identifiers, just like double(), e.g.: ```ruby books = [] books << instance_double("Book", :rspec_book, :pages => 250) books << instance_double("Book", "(Untitled)", :pages => 5000) puts books.inspect # with names, it's clearer which were actually added ``` ## Method Stubs A method stub is an implementation that returns a pre-determined value. Method stubs can be declared on test doubles or real objects using the same syntax. rspec-mocks supports 3 forms for declaring method stubs: ```ruby allow(book).to receive(:title) { "The RSpec Book" } allow(book).to receive(:title).and_return("The RSpec Book") allow(book).to receive_messages( :title => "The RSpec Book", :subtitle => "Behaviour-Driven Development with RSpec, Cucumber, and Friends") ``` You can also use this shortcut, which creates a test double and declares a method stub in one statement: ```ruby book = double("book", :title => "The RSpec Book") ``` The first argument is a name, which is used for documentation and appears in failure messages. If you don't care about the name, you can leave it out, making the combined instantiation/stub declaration very terse: ```ruby double(:foo => 'bar') ``` This is particularly nice when providing a list of test doubles to a method that iterates through them: ```ruby order.calculate_total_price(double(:price => 1.99), double(:price => 2.99)) ``` ### Stubbing a chain of methods You can use `receive_message_chain` in place of `receive` to stub a chain of messages: ```ruby allow(double).to receive_message_chain("foo.bar") { :baz } allow(double).to receive_message_chain(:foo, :bar => :baz) allow(double).to receive_message_chain(:foo, :bar) { :baz } # Given any of the above forms: double.foo.bar # => :baz ``` Chains can be arbitrarily long, which makes it quite painless to violate the Law of Demeter in violent ways, so you should consider any use of `receive_message_chain` a code smell. Even though not all code smells indicate real problems (think fluent interfaces), `receive_message_chain` still results in brittle examples. For example, if you write `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the implementation calls `foo.baz.bar`, the stub will not work. ## Consecutive return values When a stub might be invoked more than once, you can provide additional arguments to `and_return`. The invocations cycle through the list. The last value is returned for any subsequent invocations: ```ruby allow(die).to receive(:roll).and_return(1, 2, 3) die.roll # => 1 die.roll # => 2 die.roll # => 3 die.roll # => 3 die.roll # => 3 ``` To return an array in a single invocation, declare an array: ```ruby allow(team).to receive(:players).and_return([double(:name => "David")]) ``` ## Message Expectations A message expectation is an expectation that the test double will receive a message some time before the example ends. If the message is received, the expectation is satisfied. If not, the example fails. ```ruby validator = double("validator") expect(validator).to receive(:validate) { "02134" } zipcode = Zipcode.new("02134", validator) zipcode.valid? ``` ## Test Spies Verifies the given object received the expected message during the course of the test. For a message to be verified, the given object must be setup to spy on it, either by having it explicitly stubbed or by being a null object double (e.g. `double(...).as_null_object`). Convenience methods are provided to easily create null object doubles for this purpose: ```ruby spy("invitation") # => same as `double("invitation").as_null_object` instance_spy("Invitation") # => same as `instance_double("Invitation").as_null_object` class_spy("Invitation") # => same as `class_double("Invitation").as_null_object` object_spy("Invitation") # => same as `object_double("Invitation").as_null_object` ``` Verifying messages received in this way implements the Test Spy pattern. ```ruby invitation = spy('invitation') user.accept_invitation(invitation) expect(invitation).to have_received(:accept) # You can also use other common message expectations. For example: expect(invitation).to have_received(:accept).with(mailer) expect(invitation).to have_received(:accept).twice expect(invitation).to_not have_received(:accept).with(mailer) # One can specify a return value on the spy the same way one would a double. invitation = spy('invitation', :accept => true) expect(invitation).to have_received(:accept).with(mailer) expect(invitation.accept).to eq(true) ``` Note that `have_received(...).with(...)` is unable to work properly when passed arguments are mutated after the spy records the received message. For example, this does not work properly: ```ruby greeter = spy("greeter") message = "Hello" greeter.greet_with(message) message << ", World" expect(greeter).to have_received(:greet_with).with("Hello") ``` ## Nomenclature ### Mock Objects and Test Stubs The names Mock Object and Test Stub suggest specialized Test Doubles. i.e. a Test Stub is a Test Double that only supports method stubs, and a Mock Object is a Test Double that supports message expectations and method stubs. There is a lot of overlapping nomenclature here, and there are many variations of these patterns (fakes, spies, etc). Keep in mind that most of the time we're talking about method-level concepts that are variations of method stubs and message expectations, and we're applying to them to _one_ generic kind of object: a Test Double. ### Test-Specific Extension a.k.a. Partial Double, a Test-Specific Extension 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. ## Expecting Arguments ```ruby expect(double).to receive(:msg).with(*args) expect(double).to_not receive(:msg).with(*args) ``` You can set multiple expectations for the same message if you need to: ```ruby expect(double).to receive(:msg).with("A", 1, 3) expect(double).to receive(:msg).with("B", 2, 4) ``` ## Argument Matchers Arguments that are passed to `with` are compared with actual arguments received using ===. In cases in which you want to specify things about the arguments rather than the arguments themselves, you can use any of the matchers that ship with rspec-expectations. They don't all make syntactic sense (they were primarily designed for use with RSpec::Expectations), but you are free to create your own custom RSpec::Matchers. rspec-mocks also adds some keyword Symbols that you can use to specify certain kinds of arguments: ```ruby expect(double).to receive(:msg).with(no_args) expect(double).to receive(:msg).with(any_args) expect(double).to receive(:msg).with(1, any_args) # any args acts like an arg splat and can go anywhere expect(double).to receive(:msg).with(1, kind_of(Numeric), "b") #2nd argument can be any kind of Numeric expect(double).to receive(:msg).with(1, boolean(), "b") #2nd argument can be true or false expect(double).to receive(:msg).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp expect(double).to receive(:msg).with(1, anything(), "b") #2nd argument can be anything at all expect(double).to receive(:msg).with(1, duck_type(:abs, :div), "b") #2nd argument can be object that responds to #abs and #div expect(double).to receive(:msg).with(hash_including(:a => 5)) # first arg is a hash with a: 5 as one of the key-values expect(double).to receive(:msg).with(array_including(5)) # first arg is an array with 5 as one of the key-values expect(double).to receive(:msg).with(hash_excluding(:a => 5)) # first arg is a hash without a: 5 as one of the key-values expect(double).to receive(:msg).with(start_with('a')) # any matcher, custom or from rspec-expectations expect(double).to receive(:msg).with(satisfy { |data| data.dig(:a, :b, :c) == 5 }) # assert anything you want ``` ## Receive Counts ```ruby expect(double).to receive(:msg).once expect(double).to receive(:msg).twice expect(double).to receive(:msg).exactly(n).time expect(double).to receive(:msg).exactly(n).times expect(double).to receive(:msg).at_least(:once) expect(double).to receive(:msg).at_least(:twice) expect(double).to receive(:msg).at_least(n).time expect(double).to receive(:msg).at_least(n).times expect(double).to receive(:msg).at_most(:once) expect(double).to receive(:msg).at_most(:twice) expect(double).to receive(:msg).at_most(n).time expect(double).to receive(:msg).at_most(n).times ``` ## Ordering ```ruby expect(double).to receive(:msg).ordered expect(double).to receive(:other_msg).ordered # This will fail if the messages are received out of order ``` This can include the same message with different arguments: ```ruby expect(double).to receive(:msg).with("A", 1, 3).ordered expect(double).to receive(:msg).with("B", 2, 4).ordered ``` ## Setting Responses Whether you are setting a message expectation or a method stub, you can tell the object precisely how to respond. The most generic way is to pass a block to `receive`: ```ruby expect(double).to receive(:msg) { value } ``` When the double receives the `msg` message, it evaluates the block and returns the result. ```ruby expect(double).to receive(:msg).and_return(value) expect(double).to receive(:msg).exactly(3).times.and_return(value1, value2, value3) # returns value1 the first time, value2 the second, etc expect(double).to receive(:msg).and_raise(error) # `error` can be an instantiated object (e.g. `StandardError.new(some_arg)`) or a class (e.g. `StandardError`) # if it is a class, it must be instantiable with no args expect(double).to receive(:msg).and_throw(:msg) expect(double).to receive(:msg).and_yield(values, to, yield) expect(double).to receive(:msg).and_yield(values, to, yield).and_yield(some, other, values, this, time) # for methods that yield to a block multiple times ``` Any of these responses can be applied to a stub as well ```ruby allow(double).to receive(:msg).and_return(value) allow(double).to receive(:msg).and_return(value1, value2, value3) allow(double).to receive(:msg).and_raise(error) allow(double).to receive(:msg).and_throw(:msg) allow(double).to receive(:msg).and_yield(values, to, yield) allow(double).to receive(:msg).and_yield(values, to, yield).and_yield(some, other, values, this, time) ``` ## Arbitrary Handling Once in a while you'll find that the available expectations don't solve the particular problem you are trying to solve. Imagine that you expect the message to come with an Array argument that has a specific length, but you don't care what is in it. You could do this: ```ruby expect(double).to receive(:msg) do |arg| expect(arg.size).to eq 7 end ``` If the method being stubbed itself takes a block, and you need to yield to it in some special way, you can use this: ```ruby expect(double).to receive(:msg) do |&arg| begin arg.call ensure # cleanup end end ``` ## Delegating to the Original Implementation When working with a partial mock object, you may occasionally want to set a message expectation without interfering with how the object responds to the message. You can use `and_call_original` to achieve this: ```ruby expect(Person).to receive(:find).and_call_original Person.find # => executes the original find method and returns the result ``` ## Combining Expectation Details Combining the message name with specific arguments, receive counts and responses you can get quite a bit of detail in your expectations: ```ruby expect(double).to receive(:<<).with("illegal value").once.and_raise(ArgumentError) ``` While this is a good thing when you really need it, you probably don't really need it! Take care to specify only the things that matter to the behavior of your code. ## Stubbing and Hiding Constants See the [mutating constants README](https://github.com/rspec/rspec-mocks/blob/main/features/mutating_constants/README.md) for info on this feature. ## Use `before(:example)`, not `before(:context)` Stubs in `before(:context)` are not supported. The reason is that all stubs and mocks get cleared out after each example, so any stub that is set in `before(:context)` would work in the first example that happens to run in that group, but not for any others. Instead of `before(:context)`, use `before(:example)`. ## Settings mocks or stubs on any instance of a class rspec-mocks provides two methods, `allow_any_instance_of` and `expect_any_instance_of`, that will allow you to stub or mock any instance of a class. They are used in place of `allow` or `expect`: ```ruby allow_any_instance_of(Widget).to receive(:name).and_return("Wibble") expect_any_instance_of(Widget).to receive(:name).and_return("Wobble") ``` These methods add the appropriate stub or expectation to all instances of `Widget`. This feature is sometimes useful when working with legacy code, though in general we discourage its use for a number of reasons: * The `rspec-mocks` API is designed for individual object instances, but this feature operates on entire classes of objects. As a result there are some semantically confusing edge cases. For example in `expect_any_instance_of(Widget).to receive(:name).twice` it isn't clear whether each specific instance is expected to receive `name` twice, or if two receives total are expected. (It's the former.) * Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too complex. * It is the most complicated feature of `rspec-mocks`, and has historically received the most bug reports. (None of the core team actively use it, which doesn't help.) ## Further Reading There are many different viewpoints about the meaning of mocks and stubs. If you are interested in learning more, here is some recommended reading: * Mock Objects: http://www.mockobjects.com/ * Endo-Testing: http://www.ccs.neu.edu/research/demeter/related-work/extreme-programming/MockObjectsFinal.PDF * Mock Roles, Not Objects: http://www.jmock.org/oopsla2004.pdf * Test Double: http://www.martinfowler.com/bliki/TestDouble.html * Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html * Mocks aren't stubs: http://www.martinfowler.com/articles/mocksArentStubs.html ## Also see * [https://github.com/rspec/rspec](https://github.com/rspec/rspec) * [https://github.com/rspec/rspec-core](https://github.com/rspec/rspec-core) * [https://github.com/rspec/rspec-expectations](https://github.com/rspec/rspec-expectations) * [https://github.com/rspec/rspec-rails](https://github.com/rspec/rspec-rails) rspec-mocks-3.13.0/REPORT_TEMPLATE.md000066400000000000000000000021171455767030500166710ustar00rootroot00000000000000 # Report template ```ruby # frozen_string_literal: true begin require "bundler/inline" rescue LoadError => e $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler" raise e end gemfile(true) do source "https://rubygems.org" gem "rspec", "3.7.0" # Activate the gem and version you are reporting the issue against. end puts "Ruby version is: #{RUBY_VERSION}" require 'rspec/autorun' RSpec.describe 'additions' do it 'returns 2' do expect(1 + 1).to eq(2) end it 'returns 1' do expect(3 - 1).to eq(-1) end end ``` Simply copy the content of the appropriate template into a `.rb` file on your computer and make the necessary changes to demonstrate the issue. You can execute it by running `ruby rspec_report.rb` in your terminal. You can then share your executable test case as a [gist](https://gist.github.com), or simply paste the content into the issue description. rspec-mocks-3.13.0/Rakefile000066400000000000000000000015571455767030500155150ustar00rootroot00000000000000require 'bundler' Bundler.setup Bundler::GemHelper.install_tasks require 'rake' require 'rspec/core/rake_task' require 'rspec/mocks/version' require 'cucumber/rake/task' desc "Run all examples" RSpec::Core::RakeTask.new(:spec) do |t| t.ruby_opts = %w[-w] t.rspec_opts = %w[--color] end Cucumber::Rake::Task.new(:cucumber) task :clobber do rm_rf 'pkg' rm_rf 'tmp' rm_rf 'coverage' rm_rf '.yardoc' rm_rf 'doc' end namespace :clobber do desc "remove generated rbc files" task :rbc do Dir['**/*.rbc'].each { |f| File.delete(f) } end end task :default => [:spec, :cucumber] task :verify_private_key_present do private_key = File.expand_path('~/.gem/rspec-gem-private_key.pem') unless File.exist?(private_key) raise "Your private key is not present. This gem should not be built without it." end end task :build => :verify_private_key_present rspec-mocks-3.13.0/benchmarks/000077500000000000000000000000001455767030500161555ustar00rootroot00000000000000rspec-mocks-3.13.0/benchmarks/accessing_configuration_via_method_vs_cache.rb000066400000000000000000000024211455767030500274210ustar00rootroot00000000000000require 'benchmark' n = 10_000 require 'rspec/mocks' # precache config RSpec::Mocks.configuration Benchmark.benchmark do |bm| puts "#{n} times - ruby #{RUBY_VERSION}" puts puts "directly" 3.times do bm.report do n.times do original_state = RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification = true RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification = original_state end end end puts puts "with cached value" 3.times do bm.report do n.times do config = RSpec::Mocks.configuration original_state = config.temporarily_suppress_partial_double_verification config.temporarily_suppress_partial_double_verification = true config.temporarily_suppress_partial_double_verification = original_state end end end end __END__ 10000 times - ruby 2.3.1 directly 0.000000 0.000000 0.000000 ( 0.002654) 0.000000 0.000000 0.000000 ( 0.002647) 0.010000 0.000000 0.010000 ( 0.002645) with cached value 0.000000 0.000000 0.000000 ( 0.001386) 0.000000 0.000000 0.000000 ( 0.001387) 0.000000 0.000000 0.000000 ( 0.001399) rspec-mocks-3.13.0/benchmarks/allocations/000077500000000000000000000000001455767030500204655ustar00rootroot00000000000000rspec-mocks-3.13.0/benchmarks/allocations/1_object_1000_mocks.rb000066400000000000000000000066371455767030500243500ustar00rootroot00000000000000require_relative "helper" symbols = (1..1000).map { |x| :"#{x}" } benchmark_allocations do o = Object.new symbols.each do |sym| expect(o).to receive(sym) end symbols.each do |sym| o.send(sym) end RSpec::Mocks.space.verify_all RSpec::Mocks.space.reset_all end __END__ As of commit 9ee3e3adb529113bf1cc75cc4424d014f880dc47: class_plus count ---------------------------------------------------- ----- Array 22003 Proc 3001 RubyVM::Env 3001 Array 2000 String 2000 Hash 1002 RSpec::Mocks::ExpectationTarget 1000 Enumerator 1000 RSpec::Mocks::Matchers::Receive 1000 Array 1000 RSpec::Mocks::InstanceMethodStasher 1000 RSpec::Mocks::MethodDouble 1000 Array 1000 RSpec::Mocks::MessageExpectation 1000 Array 1000 Array 1000 Array 1000 Array 1000 RSpec::Mocks::Proxy::SpecificMessage 1000 RSpec::Mocks::Implementation 1000 Array 1 RSpec::Mocks::PartialDoubleProxy 1 RSpec::Mocks::ErrorGenerator 1 Array 1 After PR #936: class_plus count ---------------------------------------------------- ----- Array 21003 RubyVM::Env 3001 Proc 3001 String 2000 RSpec::Mocks::InstanceMethodStasher 1000 RSpec::Mocks::Matchers::Receive 1000 RSpec::Mocks::ExpectationTarget 1000 RSpec::Mocks::Implementation 1000 RSpec::Mocks::MessageExpectation 1000 RSpec::Mocks::MethodDouble 1000 Array 1000 Array 1000 Array 1000 Enumerator 1000 Array 1000 Array 1000 RSpec::Mocks::Proxy::SpecificMessage 1000 Array 1000 Array 1000 Hash 2 RSpec::Mocks::PartialDoubleProxy 1 Array 1 RSpec::Mocks::ErrorGenerator 1 Array 1 rspec-mocks-3.13.0/benchmarks/allocations/helper.rb000066400000000000000000000007321455767030500222730ustar00rootroot00000000000000$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__) require "allocation_stats" require 'rspec/mocks/standalone' def benchmark_allocations(burn: 1) stats = AllocationStats.new(:burn => burn).trace do yield end columns = if ENV['DETAIL'] [:sourcefile, :sourceline, :class_plus] else [:class_plus] end puts stats.allocations(:alias_paths => true).group_by(*columns).from_pwd.sort_by_size.to_text end rspec-mocks-3.13.0/benchmarks/allocations/lambda_vs_block_with_select.rb000066400000000000000000000053071455767030500265130ustar00rootroot00000000000000require 'memory_profiler' n = 10_000 def find_with_proc(argument) lambda do |lambda_arg| lambda_arg == argument end end def find(argument, lambda_arg) lambda_arg == argument end puts "#{n} items - ruby #{RUBY_VERSION}" puts puts "find_with_proc" MemoryProfiler.report do 100.times do 1.upto(n).select(&find_with_proc(50)) end end.pretty_print puts puts "find" MemoryProfiler.report do 100.times do 1.upto(n).select { |i| find(50, i) } end end.pretty_print # $ ruby benchmarks/allocations/2_lambda_ref_find.rb # 10000 items - ruby 3.2.2 # # find_with_proc # Total allocated: 29600 bytes (400 objects) # Total retained: 0 bytes (0 objects) # # allocated memory by gem # ----------------------------------- # 29600 other # # allocated memory by file # ----------------------------------- # 29600 benchmarks/allocations/2_lambda_ref_find.rb # # allocated memory by location # ----------------------------------- # 21600 benchmarks/allocations/2_lambda_ref_find.rb:22 # 8000 benchmarks/allocations/2_lambda_ref_find.rb:6 # # allocated memory by class # ----------------------------------- # 13600 Enumerator # 8000 Array # 8000 Proc # # allocated objects by gem # ----------------------------------- # 400 other # # allocated objects by file # ----------------------------------- # 400 benchmarks/allocations/2_lambda_ref_find.rb # # allocated objects by location # ----------------------------------- # 300 benchmarks/allocations/2_lambda_ref_find.rb:22 # 100 benchmarks/allocations/2_lambda_ref_find.rb:6 # # allocated objects by class # ----------------------------------- # 200 Array # 100 Enumerator # 100 Proc # # # find # Total allocated: 21600 bytes (300 objects) # Total retained: 0 bytes (0 objects) # # allocated memory by gem # ----------------------------------- # 21600 other # # allocated memory by file # ----------------------------------- # 21600 benchmarks/allocations/2_lambda_ref_find.rb # # allocated memory by location # ----------------------------------- # 21600 benchmarks/allocations/2_lambda_ref_find.rb:31 # # allocated memory by class # ----------------------------------- # 13600 Enumerator # 8000 Array # # allocated objects by gem # ----------------------------------- # 300 other # # allocated objects by file # ----------------------------------- # 300 benchmarks/allocations/2_lambda_ref_find.rb # # allocated objects by location # ----------------------------------- # 300 benchmarks/allocations/2_lambda_ref_find.rb:31 # # allocated objects by class # ----------------------------------- # 200 Array # 100 Enumerator rspec-mocks-3.13.0/benchmarks/boot_time.sh000077500000000000000000000011051455767030500204720ustar00rootroot00000000000000time (for i in {1..100}; do ruby -Ilib:../rspec-support/lib -rrspec/mocks -e ""; done) # 3 runs before our autoload changes # real 0m4.497s # user 0m3.662s # sys 0m0.677s # # real 0m4.472s # user 0m3.644s # sys 0m0.671s # # real 0m4.465s # user 0m3.640s # sys 0m0.668s # 3 runs after our autoload changes: # # real 0m4.038s # user 0m3.274s # sys 0m0.609s # # real 0m4.038s # user 0m3.274s # sys 0m0.609s # # real 0m4.038s # user 0m3.274s # sys 0m0.609s # It's modest, but that's about a 10% improvement: an average # of about 40ms to load rather than 45 ms to load. rspec-mocks-3.13.0/benchmarks/double_creation.rb000066400000000000000000000045031455767030500216420ustar00rootroot00000000000000$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) require 'benchmark' require 'rspec/mocks' n = 1000 puts "#{n} times - ruby #{RUBY_VERSION}" puts Benchmark.bm do |bm| RSpec::Mocks.setup(self) (0..9).each do |m| attrs = m.times.inject({}) {|h, x| h["method_#{x}"] = x h } bm.report("#{m} attrs") do n.times do double(attrs) end end end end # $ export OLD_REV=d483e0a893d97c7b8e612e878a9f3562a210df9f # $ git checkout $OLD_REV # $ ruby benchmarks/double_creation.rb # 1000 times - ruby 2.0.0 # # user system total real # 0 attrs 0.010000 0.000000 0.010000 ( 0.003686) # 1 attrs 0.110000 0.000000 0.110000 ( 0.143132) # 2 attrs 0.230000 0.010000 0.240000 ( 0.311358) # 3 attrs 0.400000 0.020000 0.420000 ( 0.465994) # 4 attrs 0.570000 0.010000 0.580000 ( 0.597902) # 5 attrs 0.920000 0.010000 0.930000 ( 1.060219) # 6 attrs 1.350000 0.020000 1.370000 ( 1.388386) # 7 attrs 1.770000 0.030000 1.800000 ( 1.805518) # 8 attrs 2.620000 0.030000 2.650000 ( 2.681484) # 9 attrs 3.320000 0.030000 3.350000 ( 3.380757) # # $ export NEW_REV=13e9d11542a6b60c5dc7ffa4527c98bb255d0a1f # $ git checkout $NEW_REV # $ ruby benchmarks/double_creation.rb # 1000 times - ruby 2.0.0 # # user system total real # 0 attrs 0.010000 0.000000 0.010000 ( 0.001544) # 1 attrs 0.040000 0.000000 0.040000 ( 0.043522) # 2 attrs 0.060000 0.000000 0.060000 ( 0.081742) # 3 attrs 0.090000 0.010000 0.100000 ( 0.104526) # 4 attrs 0.120000 0.010000 0.130000 ( 0.132472) # 5 attrs 0.150000 0.010000 0.160000 ( 0.162368) # 6 attrs 0.190000 0.010000 0.200000 ( 0.204610) # 7 attrs 0.220000 0.010000 0.230000 ( 0.237983) # 8 attrs 0.260000 0.010000 0.270000 ( 0.281562) # 9 attrs 0.310000 0.020000 0.330000 ( 0.334489) # # $ git log $OLD_REV..$NEW_REV --oneline # 13e9d11 Remove unused arguments from simple stub interface. # 009a697 Extract CallerFilter class to unify caller manipulation. # 46c1eb0 Introduce "simple" stub as an optimization over using a normal stub. # 4a04b3e Extract constant ArgumentListMatcher::MATCH_ALL. # 860d591 Speed up double creation with multiple attributes by caching caller. rspec-mocks-3.13.0/benchmarks/each_value_v_values_each.rb000066400000000000000000000022751455767030500234700ustar00rootroot00000000000000require 'benchmark' n = 10_000 m = 1.upto(1000).inject({}) { |m, i| m[i] = i; m } Benchmark.benchmark do |bm| puts "#{n} times - ruby #{RUBY_VERSION}" puts puts "each_value" 3.times do bm.report do n.times do m.each_value {} end end end puts puts "values.each" 3.times do bm.report do n.times do m.values.each {} end end end end # $ ruby benchmarks/values_each_v_each_value.rb # 10000 times - ruby 1.9.3 # # each_value # 0.720000 0.000000 0.720000 ( 0.720237) # 0.720000 0.000000 0.720000 ( 0.724956) # 0.730000 0.000000 0.730000 ( 0.730352) # # values.each # 0.910000 0.000000 0.910000 ( 0.917496) # 0.910000 0.010000 0.920000 ( 0.909319) # 0.910000 0.000000 0.910000 ( 0.911225) # $ ruby benchmarks/values_each_v_each_value.rb # 10000 times - ruby 2.0.0 # # each_value # 0.730000 0.000000 0.730000 ( 0.738443) # 0.720000 0.000000 0.720000 ( 0.720183) # 0.720000 0.000000 0.720000 ( 0.720866) # # values.each # 0.940000 0.000000 0.940000 ( 0.942597) # 0.960000 0.010000 0.970000 ( 0.959248) # 0.960000 0.000000 0.960000 ( 0.959099) rspec-mocks-3.13.0/benchmarks/find_original_method_early.rb000066400000000000000000000041351455767030500240450ustar00rootroot00000000000000$LOAD_PATH.unshift "./lib" require 'rspec/mocks' require "rspec/mocks/standalone" =begin This benchmark script is for troubleshooting the performance of #264. To use it, you have to edit the code in #264 a bit: wrap the call in `MethodDouble#initialize` to `find_original_method` in a conditional like `if $find_original`. That allows the code below to compare the perf of stubbing a method with the original method being found vs. not. =end require 'benchmark' n = 10_000 Foo = Class.new(Object) do n.times do |i| define_method "meth_#{i}" do end end end Benchmark.bmbm do |bm| puts "#{n} times - ruby #{RUBY_VERSION}" perform_report = lambda do |label, find_original, &create_object| dbl = create_object.call $find_original = find_original bm.report(label) do n.times do |i| dbl.stub("meth_#{i}") end end RSpec::Mocks.space.reset_all end perform_report.call("Find original - partial mock", true) { Foo.new } perform_report.call("Don't find original - partial mock", false) { Foo.new } perform_report.call("Find original - test double", true) { double } perform_report.call("Don't find original - test double", false) { double } end =begin 10000 times - ruby 1.9.3 Rehearsal ---------------------------------------------------------------------- Don't find original - partial mock 1.050000 0.020000 1.070000 ( 1.068561) Don't find original - test double 1.190000 0.010000 1.200000 ( 1.199815) Find original - partial mock 1.270000 0.010000 1.280000 ( 1.282944) Find original - test double 1.320000 0.020000 1.340000 ( 1.336136) ------------------------------------------------------------- total: 4.890000sec user system total real Don't find original - partial mock 0.990000 0.000000 0.990000 ( 1.000959) Don't find original - test double 0.930000 0.010000 0.940000 ( 0.931871) Find original - partial mock 1.040000 0.000000 1.040000 ( 1.046354) Find original - test double 0.980000 0.010000 0.990000 ( 0.983577) =end rspec-mocks-3.13.0/benchmarks/method_defined_at_any_visibility.rb000066400000000000000000000052351455767030500252470ustar00rootroot00000000000000require 'benchmark' n = 1_000_000 Foo = Class.new do 1.upto(n) do |i| define_method(:"public_method_#{i}") {} define_method(:"protected_method_#{i}") {} protected :"protected_method_#{i}" define_method(:"private_method_#{i}") {} private :"protected_method_#{i}" end end Benchmark.benchmark do |bm| puts "#{n} times - ruby #{RUBY_VERSION}" puts puts "using method_defined? and private_method_defined?" puts [:public, :protected, :private, :undefined].each do |vis| puts " - #{vis} methods" 3.times do GC.start bm.report do n.times do |i| name = :"#{vis}_method_#{i}" Foo.method_defined?(name) || Foo.private_method_defined?(name) end end end end puts puts "using public_method_defined?, protected_method_defined? and private_method_defined?" puts [:public, :protected, :private, :undefined].each do |vis| puts " - #{vis} methods" 3.times do GC.start bm.report do n.times do |i| name = :"#{vis}_method_#{i}" Foo.public_method_defined?(name) || Foo.protected_method_defined?(name) Foo.private_method_defined?(name) end end end end end =begin 1000000 times - ruby 2.0.0 using method_defined? and private_method_defined? - public methods 1.410000 0.040000 1.450000 ( 1.462588) 1.380000 0.000000 1.380000 ( 1.372015) 1.370000 0.000000 1.370000 ( 1.372362) - protected methods 1.410000 0.000000 1.410000 ( 1.402750) 1.440000 0.000000 1.440000 ( 1.442719) 1.460000 0.010000 1.470000 ( 1.464763) - private methods 1.390000 0.000000 1.390000 ( 1.393956) 1.340000 0.000000 1.340000 ( 1.349340) 1.360000 0.000000 1.360000 ( 1.361910) - undefined methods 3.260000 0.050000 3.310000 ( 3.316372) 1.260000 0.010000 1.270000 ( 1.266557) 1.250000 0.000000 1.250000 ( 1.248734) using public_method_defined?, protected_method_defined? and private_method_defined? - public methods 1.550000 0.000000 1.550000 ( 1.550655) 1.540000 0.010000 1.550000 ( 1.543906) 1.540000 0.000000 1.540000 ( 1.538267) - protected methods 1.590000 0.000000 1.590000 ( 1.598310) 1.600000 0.000000 1.600000 ( 1.595205) 1.600000 0.000000 1.600000 ( 1.604186) - private methods 1.530000 0.000000 1.530000 ( 1.530080) 1.560000 0.000000 1.560000 ( 1.562656) 1.560000 0.000000 1.560000 ( 1.569161) - undefined methods 1.300000 0.000000 1.300000 ( 1.298066) 1.310000 0.000000 1.310000 ( 1.310737) 1.290000 0.000000 1.290000 ( 1.288307) =end rspec-mocks-3.13.0/benchmarks/thread_safety.rb000066400000000000000000000011441455767030500213240ustar00rootroot00000000000000$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) require 'benchmark' require 'rspec/mocks' Benchmark.bm do |bm| bm.report("fetching a proxy") do RSpec::Mocks.with_temporary_scope do o = Object.new 100_000.times { RSpec::Mocks.space.proxy_for(o) } end end end # Without synchronize (not thread-safe): # # user system total real # fetching a proxy 0.120000 0.000000 0.120000 ( 0.141333) # # With synchronize (thread-safe): # user system total real # fetching a proxy 0.180000 0.000000 0.180000 ( 0.189553) rspec-mocks-3.13.0/benchmarks/transfer_nested_constants.rb000066400000000000000000000043541455767030500237720ustar00rootroot00000000000000$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__)) require 'benchmark' require 'rspec/mocks' N = ENV.fetch('N', 10_000).to_i M = ENV.fetch('M', 5).to_i puts "#{N} times, #{M} constants - ruby #{RUBY_VERSION}" puts class A M.times do |x| const_set("C#{x}", Object.new) end end Benchmark.bm(20) do |bm| RSpec::Mocks.setup(self) bm.report("with constants") do N.times do class_double('A').as_stubbed_const(:transfer_nested_constants => true) end end bm.report("without constants") do N.times do class_double('A').as_stubbed_const(:transfer_nested_constants => false) end end end # > for n in 1 10000; do for m in 0 5 100; do echo; \ # env N=$n M=$m ruby benchmarks/transfer_nested_constants.rb; \ # echo; done; done # # 1 times, 0 constants - ruby 2.0.0 # # user system total real # with constants 0.000000 0.000000 0.000000 ( 0.000180) # without constants 0.000000 0.000000 0.000000 ( 0.000071) # # # 1 times, 5 constants - ruby 2.0.0 # # user system total real # with constants 0.000000 0.000000 0.000000 ( 0.000197) # without constants 0.000000 0.000000 0.000000 ( 0.000123) # # # 1 times, 100 constants - ruby 2.0.0 # # user system total real # with constants 0.000000 0.000000 0.000000 ( 0.000433) # without constants 0.000000 0.000000 0.000000 ( 0.000115) # # # 10000 times, 0 constants - ruby 2.0.0 # # user system total real # with constants 0.900000 0.020000 0.920000 ( 0.935583) # without constants 0.660000 0.010000 0.670000 ( 0.680178) # # # 10000 times, 5 constants - ruby 2.0.0 # # user system total real # with constants 1.080000 0.020000 1.100000 ( 1.114722) # without constants 0.720000 0.020000 0.740000 ( 0.741976) # # # 10000 times, 100 constants - ruby 2.0.0 # # user system total real # with constants 3.870000 0.110000 3.980000 ( 4.000176) # without constants 0.930000 0.010000 0.940000 ( 0.947197) rspec-mocks-3.13.0/cucumber.yml000066400000000000000000000004341455767030500163710ustar00rootroot00000000000000<% USE_TILDE_TAGS = !defined?(::RUBY_ENGINE_VERSION) || (::RUBY_ENGINE_VERSION < '2.0.0') NOT_WIP_TAG = USE_TILDE_TAGS ? '~@wip' : '"not @wip"' %> default: --require features --tags <%= NOT_WIP_TAG %> features --format progress wip: --require features --tags @wip:3 --wip features rspec-mocks-3.13.0/features/000077500000000000000000000000001455767030500156565ustar00rootroot00000000000000rspec-mocks-3.13.0/features/.nav000066400000000000000000000022461455767030500164470ustar00rootroot00000000000000- basics: - test_doubles.feature - allowing_messages.feature - expecting_messages.feature - partial_test_doubles.feature - null_object_doubles.feature - spies.feature - scope.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 - wrapping_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: - minitest.feature - any_test_framework.feature - standalone.feature - Changelog rspec-mocks-3.13.0/features/README.md000066400000000000000000000054221455767030500171400ustar00rootroot00000000000000# RSpec Mocks 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. ## 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. ## 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: ```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 invalid arguments. ```ruby book = instance_double("Book", :pages => 250) ``` [Verifying doubles](./rspec-mocks/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: ```ruby allow(die).to receive(:roll) { 3 } ``` 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: ```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) 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. ## 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). rspec-mocks-3.13.0/features/basics/000077500000000000000000000000001455767030500171225ustar00rootroot00000000000000rspec-mocks-3.13.0/features/basics/allowing_messages.feature000066400000000000000000000025321455767030500242040ustar00rootroot00000000000000Feature: 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 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 rspec-mocks-3.13.0/features/basics/expecting_messages.feature000066400000000000000000000077311455767030500243640ustar00rootroot00000000000000Feature: 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 Scenario: Failing positive message expectation with a custom failure message Given a file named "example_spec.rb" with: """ruby RSpec.describe "An unfulfilled positive message expectation" do it "triggers a failure" do dbl = double expect(dbl).to receive(:foo), "dbl never calls :foo" end end """ When I run `rspec example_spec.rb --format documentation` Then the output should contain: """ 1) An unfulfilled positive message expectation triggers a failure Failure/Error: expect(dbl).to receive(:foo), "dbl never calls :foo" dbl never calls :foo """ Scenario: Failing negative message expectation with a custom failure message Given a file named "example_spec.rb" with: """ruby RSpec.describe "A negative message expectation" do it "fails when the message is received" do dbl = double expect(dbl).not_to receive(:foo), "dbl called :foo but is not supposed to" dbl.foo end end """ When I run `rspec example_spec.rb --format documentation` Then the output should contain: """ 1) A negative message expectation fails when the message is received Failure/Error: dbl.foo dbl called :foo but is not supposed to """ rspec-mocks-3.13.0/features/basics/null_object_doubles.feature000066400000000000000000000026011455767030500245130ustar00rootroot00000000000000Feature: 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 black 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 rspec-mocks-3.13.0/features/basics/partial_test_doubles.feature000066400000000000000000000046421455767030500247150ustar00rootroot00000000000000Feature: 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 rspec-mocks-3.13.0/features/basics/scope.feature000066400000000000000000000072611455767030500216160ustar00rootroot00000000000000Feature: 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}") | | # was originally created in one example but has leaked into another example and can no longer be used. | rspec-mocks-3.13.0/features/basics/spies.feature000066400000000000000000000132741455767030500216310ustar00rootroot00000000000000Feature: 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 arrange-act-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. Spies 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. Note: `have_received(...).with(...)` is unable to work properly when passed arguments are mutated after the spy records the received message. Scenario: Using a spy Given a file named "spy_spec.rb" with: """ruby RSpec.describe "have_received" do it "passes when the message has been received" do invitation = spy('invitation') invitation.deliver expect(invitation).to have_received(:deliver) end end """ When I run `rspec 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 spy" do invitation = spy('invitation') 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 spy 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 invitation" do let(:invitation) { spy("invitation") } 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 satisfied" 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 satisfied" 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 invitation 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 invitation fails when an order constraint is not satisfied | | Failure/Error: expect(invitation).to have_received(:deliver).with("foo@example.com").ordered | | # received :deliver out of order | rspec-mocks-3.13.0/features/basics/test_doubles.feature000066400000000000000000000032051455767030500231730ustar00rootroot00000000000000Feature: 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 "A test 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: """ # 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 "A test double" do it "returns canned responses from the methods named in the provided hash" 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 rspec-mocks-3.13.0/features/configuring_responses/000077500000000000000000000000001455767030500222715ustar00rootroot00000000000000rspec-mocks-3.13.0/features/configuring_responses/README.md000066400000000000000000000020071455767030500235470ustar00rootroot00000000000000# Configuring responses 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. * `and_return` * `and_raise` * `and_invoke` * `and_throw` * `and_yield` * `and_call_original` * `and_wrap_original` In addition, you can provide a [block implementation](./configuring-responses/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. rspec-mocks-3.13.0/features/configuring_responses/block_implementation.feature000066400000000000000000000107401455767030500300470ustar00rootroot00000000000000Feature: 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 declarative 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 rspec-mocks-3.13.0/features/configuring_responses/calling_the_original_implementation.feature000066400000000000000000000035141455767030500331130ustar00rootroot00000000000000Feature: 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 overridden 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 overridden 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 rspec-mocks-3.13.0/features/configuring_responses/mixed_responses.feature000066400000000000000000000020551455767030500270570ustar00rootroot00000000000000Feature: Mixed responses Use `and_invoke` to invoke a callable when a message is received. Pass `and_invoke` multiple callables to have different behavior for consecutive calls. The final callable will continue to be called if the message is received additional times. Note: The invoked callable will be invoked with the calls arguments, so it is recommended to use a `lambda` or similar with the same arity as your method but you can use a `proc` if you do not care about arity (e.g. when raising). Scenario: Mixed responses Given a file named "raises_and_then_returns.rb" with: """ruby RSpec.describe "when the method is called multiple times" do it "raises and then later returns a value" do dbl = double allow(dbl).to receive(:foo).and_invoke(lambda { raise "failure" }, lambda { true }) expect { dbl.foo }.to raise_error("failure") expect(dbl.foo).to eq(true) end end """ When I run `rspec raises_and_then_returns.rb` Then the examples should all pass rspec-mocks-3.13.0/features/configuring_responses/raising_an_error.feature000066400000000000000000000015631455767030500271760ustar00rootroot00000000000000Feature: 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 """ rspec-mocks-3.13.0/features/configuring_responses/returning_a_value.feature000066400000000000000000000037371455767030500273710ustar00rootroot00000000000000Feature: 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. Note - If you are looking for documentation for configuring responses from `allow_any_instance_of`, please see the [working with legacy code](../working-with-legacy-code/any-instance) documentation. 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) # begins to repeat last value expect(dbl.foo).to eq(3) end end """ When I run `rspec multiple_calls_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/configuring_responses/throwing.feature000066400000000000000000000017171455767030500255150ustar00rootroot00000000000000Feature: 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 rspec-mocks-3.13.0/features/configuring_responses/wrapping_the_original_implementation.feature000066400000000000000000000057271455767030500333410ustar00rootroot00000000000000Feature: Wrapping the original implementation Use `and_wrap_original` to modify a partial double's original response. This can be useful when you want to utilise an external object but mutate its response. For example if an API returns a large amount of data and for test purposes you'd like to trim it down. You can also use it to configure the default response for most arguments, and then override that for specific arguments using `with`. Note: `and_wrap_original` is only supported on partial doubles, as normal test doubles do not have an original implementation. Background: Given a file named "lib/api.rb" with: """ruby class API def self.solve_for(x) (1..x).to_a end end """ Scenario: `and_wrap_original` wraps the original partial double response Given a file named "spec/and_wrap_original_spec.rb" with: """ruby require 'api' RSpec.describe "and_wrap_original" do it "responds as it normally would, modified by the block" do expect(API).to receive(:solve_for).and_wrap_original { |m, *args| m.call(*args).first(5) } expect(API.solve_for(100)).to eq [1,2,3,4,5] end end """ When I run `rspec spec/and_wrap_original_spec.rb` Then the examples should all pass Scenario: `and_wrap_original` can configure a default response that can be overridden for specific args Given a file named "spec/and_wrap_original_spec.rb" with: """ruby require 'api' RSpec.describe "and_wrap_original" do it "can be overridden for specific arguments using #with" do allow(API).to receive(:solve_for).and_wrap_original { |m, *args| m.call(*args).first(5) } allow(API).to receive(:solve_for).with(2).and_return([3]) expect(API.solve_for(20)).to eq [1,2,3,4,5] expect(API.solve_for(2)).to eq [3] end end """ When I run `rspec spec/and_wrap_original_spec.rb` Then the examples should all pass @kw-arguments Scenario: `and_wrap_original` can configure a default response that can be overridden for specific keyword arguments Given a file named "lib/kw_api.rb" with: """ruby class API def self.solve_for(x: 1, y: 2) (x..y).to_a end end """ Given a file named "spec/and_wrap_original_spec.rb" with: """ruby require 'kw_api' RSpec.describe "and_wrap_original" do it "can be overridden for specific arguments using #with" do allow(API).to receive(:solve_for).and_wrap_original { |m, **kwargs| m.call(**kwargs).first(5) } allow(API).to receive(:solve_for).with(x: 3, y: 4).and_return([3]) expect(API.solve_for(x: 1, y: 20)).to eq [1,2,3,4,5] expect(API.solve_for(y: 20)).to eq [1,2,3,4,5] expect(API.solve_for(x: 3, y: 4)).to eq [3] end end """ When I run `rspec spec/and_wrap_original_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/configuring_responses/yielding.feature000066400000000000000000000047121455767030500254560ustar00rootroot00000000000000Feature: 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: """ # 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: """ # 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 rspec-mocks-3.13.0/features/mutating_constants/000077500000000000000000000000001455767030500216025ustar00rootroot00000000000000rspec-mocks-3.13.0/features/mutating_constants/README.md000066400000000000000000000040441455767030500230630ustar00rootroot00000000000000# Mutating Constants ### Stubbing Support is provided for stubbing constants. Like with method stubs, the stubbed constants will be restored to their original state when an example completes. ``` ruby stub_const("Foo", fake_foo) Foo # => fake_foo ``` Stubbed constant names must be fully qualified; the current module nesting is not considered. ``` ruby module MyGem class SomeClass; end end module MyGem describe "Something" do let(:fake_class) { Class.new } it "accidentally stubs the wrong constant" do # this stubs ::SomeClass (in the top-level namespace), # not MyGem::SomeClass like you probably mean. stub_const("SomeClass", fake_class) end it "stubs the right constant" do stub_const("MyGem::SomeClass", fake_class) end end end ``` When you stub a constant that is a module or a class, nested constants on the original module or class are not transferred by default, but you can use the `:transfer_nested_constants` option to tell rspec-mocks to transfer them: ``` ruby class CardDeck SUITS = [:Spades, :Diamonds, :Clubs, :Hearts] NUM_CARDS = 52 end fake_class = Class.new stub_const("CardDeck", fake_class) CardDeck # => fake_class CardDeck::SUITS # => raises uninitialized constant error CardDeck::NUM_CARDS # => raises uninitialized constant error stub_const("CardDeck", fake_class, :transfer_nested_constants => true) CardDeck::SUITS # => [:Spades, :Diamonds, :Clubs, :Hearts] CardDeck::NUM_CARDS # => 52 stub_const("CardDeck", fake_class, :transfer_nested_constants => [:SUITS]) CardDeck::SUITS # => [:Spades, :Diamonds, :Clubs, :Hearts] CardDeck::NUM_CARDS # => raises uninitialized constant error ``` ### Hiding Support is also provided for hiding constants. Hiding a constant temporarily removes it; it is restored to its original value after the test completes. ```ruby FOO = 42 hide_const("FOO") FOO # => NameError: uninitialized constant FOO ``` Like stubbed constants, names must be fully qualified. Hiding constants that are already undefined has no effect. ```ruby hide_const("NO_OP") ``` rspec-mocks-3.13.0/features/mutating_constants/hide_defined_constant.feature000066400000000000000000000034371455767030500274660ustar00rootroot00000000000000Feature: Hide Defined Constant Use `hide_const` to remove a constant for the duration of a test. Scenario: Hide top-level constant Given a file named "hide_const_spec.rb" with: """ruby FOO = 7 RSpec.describe "hiding FOO" do it "can hide FOO" do hide_const("FOO") expect { FOO }.to raise_error(NameError) end it "restores the hidden constant when the example completes" do expect(FOO).to eq(7) end end """ When I run `rspec hide_const_spec.rb` Then the examples should all pass Scenario: Hide nested constant Given a file named "hide_const_spec.rb" with: """ruby module MyGem class SomeClass FOO = 7 end end module MyGem RSpec.describe SomeClass do it "hides the nested constant when it is fully qualified" do hide_const("MyGem::SomeClass::FOO") expect { SomeClass::FOO }.to raise_error(NameError) end it "restores the hidden constant when the example completes" do expect(MyGem::SomeClass::FOO).to eq(7) end end end """ When I run `rspec hide_const_spec.rb` Then the examples should all pass Scenario: Hiding undefined constant Given a file named "hide_const_spec.rb" with: """ruby RSpec.describe "hiding UNDEFINED_CONSTANT" do it "has no effect" do hide_const("UNDEFINED_CONSTANT") expect { UNDEFINED_CONSTANT }.to raise_error(NameError) end it "is still undefined after the example completes" do expect { UNDEFINED_CONSTANT }.to raise_error(NameError) end end """ When I run `rspec hide_const_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/mutating_constants/hide_undefined_constant.feature000066400000000000000000000015341455767030500300250ustar00rootroot00000000000000Feature: Hide Undefined Constant Hiding a constant that is already undefined is a no-op. This can be useful when a spec file may run in either an isolated environment (e.g. when running one spec file) or in a full environment with all parts of your code base loaded (e.g. when running your entire suite). Scenario: Hiding undefined constant Given a file named "hide_const_spec.rb" with: """ruby RSpec.describe "hiding UNDEFINED_CONSTANT" do it "has no effect" do hide_const("UNDEFINED_CONSTANT") expect { UNDEFINED_CONSTANT }.to raise_error(NameError) end it "is still undefined after the example completes" do expect { UNDEFINED_CONSTANT }.to raise_error(NameError) end end """ When I run `rspec hide_const_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/mutating_constants/stub_defined_constant.feature000066400000000000000000000043431455767030500275270ustar00rootroot00000000000000Feature: Stub Defined Constant Use `stub_const` to stub constants. When the constant is already defined, the stubbed value will replace the original value for the duration of the example. Scenario: Stub top-level constant Given a file named "stub_const_spec.rb" with: """ruby FOO = 7 RSpec.describe "stubbing FOO" do it "can stub FOO with a different value" do stub_const("FOO", 5) expect(FOO).to eq(5) end it "restores the stubbed constant when the example completes" do expect(FOO).to eq(7) end end """ When I run `rspec stub_const_spec.rb` Then the examples should all pass Scenario: Stub nested constant Given a file named "stub_const_spec.rb" with: """ruby module MyGem class SomeClass FOO = 7 end end module MyGem RSpec.describe SomeClass do it "stubs the nested constant when it is fully qualified" do stub_const("MyGem::SomeClass::FOO", 5) expect(SomeClass::FOO).to eq(5) end end end """ When I run `rspec stub_const_spec.rb` Then the examples should all pass Scenario: Transfer nested constants Given a file named "stub_const_spec.rb" with: """ruby module MyGem class SomeClass FOO = 7 end end module MyGem RSpec.describe SomeClass do let(:fake_class) { Class.new } it "does not transfer nested constants by default" do stub_const("MyGem::SomeClass", fake_class) expect { SomeClass::FOO }.to raise_error(NameError) end it "transfers nested constants when using :transfer_nested_constants => true" do stub_const("MyGem::SomeClass", fake_class, :transfer_nested_constants => true) expect(SomeClass::FOO).to eq(7) end it "can specify a list of nested constants to transfer" do stub_const("MyGem::SomeClass", fake_class, :transfer_nested_constants => [:FOO]) expect(SomeClass::FOO).to eq(7) end end end """ When I run `rspec stub_const_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/mutating_constants/stub_undefined_constant.feature000066400000000000000000000031521455767030500300670ustar00rootroot00000000000000Feature: Stub Undefined Constant Use `stub_const` to stub constants. When the constant is not already defined, all the necessary intermediary modules will be dynamically created. When the example completes, the intermediary module constants will be removed to return the constant state to how it started. Scenario: Stub top-level constant Given a file named "stub_const_spec.rb" with: """ruby RSpec.describe "stubbing FOO" do it "can stub undefined constant FOO" do stub_const("FOO", 5) expect(FOO).to eq(5) end it "undefines the constant when the example completes" do expect { FOO }.to raise_error(NameError) end end """ When I run `rspec stub_const_spec.rb` Then the examples should all pass Scenario: Stub nested constant Given a file named "stub_const_spec.rb" with: """ruby module MyGem class SomeClass end end module MyGem RSpec.describe SomeClass do it "can stub an arbitrarily deep constant that is undefined" do expect(defined?(SomeClass::A)).to be_falsey stub_const("MyGem::SomeClass::A::B::C", 3) expect(SomeClass::A::B::C).to eq(3) expect(SomeClass::A).to be_a(Module) end it 'undefines the intermediary constants that were dynamically created' do expect(defined?(SomeClass)).to be_truthy expect(defined?(SomeClass::A)).to be_falsey end end end """ When I run `rspec stub_const_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/old_syntax/000077500000000000000000000000001455767030500200425ustar00rootroot00000000000000rspec-mocks-3.13.0/features/old_syntax/README.md000066400000000000000000000027001455767030500213200ustar00rootroot00000000000000# Old Syntax Historically, rspec-mocks has used a monkey-patched syntax to allow you to mock or stub any object: ```ruby obj.stub(:foo).and_return(15) obj.should_receive(:bar) ``` Unfortunately, this is prone to weird, confusing failures when applied to [delegate/proxy objects](https://rspec.info/blog/2012/06/rspecs-new-expectation-syntax#delegation_issues). For a method like `stub` to work properly, it must be defined on every object in the system, but RSpec does not own every object in the system and cannot ensure that it always works consistently. For this reason, in RSpec 2.14, we introduced a new syntax that avoids monkey patching altogether. It's the syntax shown in all examples of this documentation outside of this directory. As of RSpec 3, we consider this to be the main, recommended syntax of rspec- mocks. The old monkey-patched syntax continues to work, but you will get a deprecation warning if you use it without explicitly opting-in to it: ```ruby # If you're using rspec-core: RSpec.configure do |config| config.mock_with :rspec do |mocks| mocks.syntax = :should end end # Or, if you're using rspec-mocks in another context: RSpec::Mocks.configuration.syntax = :should ``` We have no plans to ever kill the old syntax, but we may extract it into an external gem in RSpec 4. If you have an old project that uses the old syntax and you want to update it to the current syntax, checkout [transpec](http://yujinakayama.me/transpec/). rspec-mocks-3.13.0/features/old_syntax/any_instance.feature000066400000000000000000000070601455767030500240750ustar00rootroot00000000000000@allow-old-syntax Feature: `any_instance` `any_instance` is the old way to stub or mock any instance of a class but carries the baggage of a global monkey patch on all classes. Note that we [generally recommend against](../working-with-legacy-code/any-instance) using this feature. 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 """ And a file named ".rspec" with: """ --require spec_helper """ Scenario: Stub a method on any instance of a class Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "Stubbing a method with any_instance" do it "returns the specified value on any instance of the class" do Object.any_instance.stub(:foo).and_return(:return_value) o = Object.new expect(o.foo).to eq(:return_value) end end """ When I run `rspec spec/example_spec.rb` Then the examples should all pass Scenario: Stub multiple methods on any instance of a class Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "Stubbing multiple methods with any_instance" do it "returns the specified values for the given messages" do Object.any_instance.stub(:foo => 'foo', :bar => 'bar') o = Object.new expect(o.foo).to eq('foo') expect(o.bar).to eq('bar') end end """ When I run `rspec spec/example_spec.rb` Then the examples should all pass Scenario: Stubbing any instance of a class with specific arguments Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "Stubbing any instance with arguments" do it "returns the stubbed value when arguments match" do Object.any_instance.stub(:foo).with(:param_one, :param_two).and_return(:result_one) Object.any_instance.stub(: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 """ When I run `rspec spec/example_spec.rb` Then the examples should all pass Scenario: Block implementation is passed the receiver as first arg Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "Stubbing any instance of a class" do it 'yields the receiver to the block implementation' do String.any_instance.stub(:slice) do |value, start, length| value[start, length] end expect('string'.slice(2, 3)).to eq('rin') end end """ When I run `rspec spec/example_spec.rb` Then the examples should all pass Scenario: Expect a message on any instance of a class Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "Expecting a message on any instance of a class" do before do Object.any_instance.should_receive(:foo) end it "passes when an instance receives the message" do Object.new.foo end it "fails when no instance receives the message" do Object.new.to_s end end """ When I run `rspec spec/example_spec.rb` Then it should fail with the following output: | 2 examples, 1 failure | | Exactly one instance should have received the following message(s) but didn't: foo | rspec-mocks-3.13.0/features/old_syntax/should_receive.feature000066400000000000000000000061641455767030500244260ustar00rootroot00000000000000@allow-old-syntax Feature: `should_receive` `should_receive` is the old way to [expect messages](../basics/expecting-messages) but carries the baggage of a global monkey patch on all objects. It supports the same fluent interface for [setting constraints](../setting-constraints) and [configuring responses](../configuring-responses). Similarly, you can use `should_not_receive` to set a negative message expectation. 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 """ And a file named ".rspec" with: """ --require spec_helper """ Scenario: Failing positive message expectation Given a file named "spec/unfulfilled_message_expectation_spec.rb" with: """ruby RSpec.describe "An unfulfilled message expectation" do it "triggers a failure" do dbl = double("Some Collaborator") dbl.should_receive(:foo) end end """ When I run `rspec spec/unfulfilled_message_expectation_spec.rb` Then it should fail with: """ 1) An unfulfilled message expectation triggers a failure Failure/Error: dbl.should_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 "spec/fulfilled_message_expectation_spec.rb" with: """ruby RSpec.describe "A fulfilled message expectation" do it "passes" do dbl = double("Some Collaborator") dbl.should_receive(:foo) dbl.foo end end """ When I run `rspec spec/fulfilled_message_expectation_spec.rb` Then the examples should all pass Scenario: Failing negative message expectation Given a file named "spec/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 dbl.should_not_receive(:foo) dbl.foo end end """ When I run `rspec spec/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 "spec/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 dbl.should_not_receive(:foo) end end """ When I run `rspec spec/negative_message_expectation_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/old_syntax/stub.feature000066400000000000000000000033101455767030500223710ustar00rootroot00000000000000@allow-old-syntax Feature: `stub` `stub` is the old way to [allow messages](../basics/allowing-messages) but carries the baggage of a global monkey patch on all objects. It supports the same fluent interface for [setting constraints](../setting-constraints) and [configuring responses](../configuring-responses). You can also pass `stub` a hash of message/return-value pairs, which acts like `allow(obj).to receive_messages(hash)`, but does not support further customization through the fluent interface. 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 """ And a file named ".rspec" with: """ --require spec_helper """ Scenario: Stub a method Given a file named "spec/stub_spec.rb" with: """ruby RSpec.describe "Stubbing a method" do it "configures how the object responds" do dbl = double dbl.stub(:foo).and_return(13) expect(dbl.foo).to eq(13) end end """ When I run `rspec spec/stub_spec.rb` Then the examples should all pass Scenario: Stub multiple methods by passing a hash Given a file named "spec/stub_multiple_methods_spec.rb" with: """ruby RSpec.describe "Stubbing multiple methods" do it "stubs each named method with the given return value" do dbl = double dbl.stub(:foo => 13, :bar => 10) expect(dbl.foo).to eq(13) expect(dbl.bar).to eq(10) end end """ When I run `rspec spec/stub_multiple_methods_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/old_syntax/stub_chain.feature000066400000000000000000000041671455767030500235460ustar00rootroot00000000000000@allow-old-syntax Feature: `stub_chain` `stub_chain` is the old way to [allow a message chain](../working-with-legacy-code/message-chains) but carries the baggage of a global monkey patch on all objects. As with `receive_message_chain`, use with care; we recommend treating usage of `stub_chain` as a code smell. 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 """ And a file named ".rspec" with: """ --require spec_helper """ Scenario: Use `stub_chain` on a double Given a file named "spec/stub_chain_spec.rb" with: """ruby RSpec.describe "Using stub_chain on a double" do let(:dbl) { double } example "using a string and a block" do dbl.stub_chain("foo.bar") { :baz } expect(dbl.foo.bar).to eq(:baz) end example "using symbols and a hash" do dbl.stub_chain(:foo, :bar => :baz) expect(dbl.foo.bar).to eq(:baz) end example "using symbols and a block" do dbl.stub_chain(:foo, :bar) { :baz } expect(dbl.foo.bar).to eq(:baz) end end """ When I run `rspec spec/stub_chain_spec.rb` Then the examples should all pass Scenario: Use `stub_chain` on any instance of a class Given a file named "spec/stub_chain_spec.rb" with: """ruby RSpec.describe "Using any_instance.stub_chain" do example "using a string and a block" do Object.any_instance.stub_chain("foo.bar") { :baz } expect(Object.new.foo.bar).to eq(:baz) end example "using symbols and a hash" do Object.any_instance.stub_chain(:foo, :bar => :baz) expect(Object.new.foo.bar).to eq(:baz) end example "using symbols and a block" do Object.any_instance.stub_chain(:foo, :bar) { :baz } expect(Object.new.foo.bar).to eq(:baz) end end """ When I run `rspec spec/stub_chain_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/old_syntax/unstub.feature000066400000000000000000000031251455767030500227400ustar00rootroot00000000000000@allow-old-syntax Feature: Using `unstub` `unstub` removes a method stub, essentially cleaning up the method stub early, rather than waiting for the cleanup that runs at the end of the example. The newer non-monkey-patching syntax does not have a direct equivalent but in most situations you can achieve the same behavior using [`and_call_original`](../configuring-responses/calling-the-original-implementation). The difference is that `obj.unstub(:foo)` completely cleans up the `foo` method stub, whereas `allow(obj).to receive(:foo).and_call_original` continues to observe calls to the method (important when you are using [spies](../basics/spies)), which could affect the method's behavior if it does anything with `caller` as it will include additional rspec stack frames. 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 """ And a file named ".rspec" with: """ --require spec_helper """ Scenario: Unstub a method Given a file named "spec/unstub_spec.rb" with: """ruby RSpec.describe "Unstubbing a method" do it "restores the original behavior" do string = "hello world" string.stub(:reverse) { "hello dlrow" } expect { string.unstub(:reverse) }.to change { string.reverse }.from("hello dlrow").to("dlrow olleh") end end """ When I run `rspec spec/unstub_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/outside_rspec/000077500000000000000000000000001455767030500205265ustar00rootroot00000000000000rspec-mocks-3.13.0/features/outside_rspec/any_test_framework.feature000066400000000000000000000120711455767030500260070ustar00rootroot00000000000000Feature: Integrating with any test framework rspec-mocks is a stand-alone gem that can be integrated with any test framework. The example below demonstrates using rspec-mocks with [minitest](http://docs.seattlerb.org/minitest/), but these steps would apply when integrating rspec-mocks with any library or framework: * Include `RSpec::Mocks::ExampleMethods` in your test context. This provides rspec-mocks' API. * Call `RSpec::Mocks.setup` before a test begins. * Call `RSpec::Mocks.verify` after a test completes to verify message expectations. Note that this step is optional; rspec-core, for example, skips this when an example has already failed. * Call `RSpec::Mocks.teardown` after a test completes (and after `verify`) to cleanup. This _must_ be called, even if an error has occurred, so it generally goes in an `ensure` clause. Note: if you are using minitest, you'll probably want to use the built-in [minitest integration](./minitest). Scenario: Use rspec-mocks with Minitest Given a file named "test/test_helper.rb" with: """ruby require 'minitest/autorun' require 'rspec/mocks' module MinitestRSpecMocksIntegration include ::RSpec::Mocks::ExampleMethods def before_setup ::RSpec::Mocks.setup super end def after_teardown super ::RSpec::Mocks.verify ensure ::RSpec::Mocks.teardown end end Minitest::Test.send(:include, MinitestRSpecMocksIntegration) """ And a file named "test/rspec_mocks_test.rb" with: """ruby require 'test_helper' class RSpecMocksTest < Minitest::Test def test_passing_positive_expectation dbl = double expect(dbl).to receive(:message) dbl.message end def test_failing_positive_expectation dbl = double expect(dbl).to receive(:message) end def test_passing_negative_expectation dbl = double expect(dbl).to_not receive(:message) end def test_failing_negative_expectation dbl = double expect(dbl).to_not receive(:message) dbl.message end def test_passing_positive_spy_expectation bond = spy bond.james expect(bond).to have_received(:james) end def test_failing_positive_spy_expectation bond = spy expect(bond).to have_received(:james) end def test_passing_negative_spy_expectation bond = spy expect(bond).not_to have_received(:james) end def test_failing_negative_spy_expectation bond = spy bond.james expect(bond).not_to have_received(:james) end end """ When I run `ruby -Itest test/rspec_mocks_test.rb` Then it should fail with the following output: | 1) Error: | | RSpecMocksTest#test_failing_negative_expectation: | | RSpec::Mocks::MockExpectationError: (Double (anonymous)).message(no args) | | expected: 0 times with any arguments | | received: 1 time | | | | 2) Error: | | RSpecMocksTest#test_failing_positive_expectation: | | RSpec::Mocks::MockExpectationError: (Double (anonymous)).message(*(any args)) | | expected: 1 time with any arguments | | received: 0 times with any arguments | | | | 3) Error: | | RSpecMocksTest#test_failing_positive_spy_expectation: | | RSpec::Mocks::MockExpectationError: (Double (anonymous)).james(*(any args)) | | expected: 1 time with any arguments | | received: 0 times with any arguments | | | | 4) Error: | | RSpecMocksTest#test_failing_negative_spy_expectation: | | RSpec::Mocks::MockExpectationError: (Double (anonymous)).james(no args) | | expected: 0 times with any arguments | | received: 1 time | | | | 8 runs, 0 assertions, 0 failures, 4 errors, 0 skips | rspec-mocks-3.13.0/features/outside_rspec/minitest.feature000066400000000000000000000237131455767030500237450ustar00rootroot00000000000000Feature: Integrating with Minitest To use rspec-mocks with minitest, simply require `rspec/mocks/minitest_integration`. Scenario: Use rspec-mocks with Minitest::Test Given a file named "test/rspec_mocks_test.rb" with: """ruby require 'minitest/autorun' require 'rspec/mocks/minitest_integration' class RSpecMocksTest < Minitest::Test def test_passing_positive_expectation dbl = double expect(dbl).to receive(:message) dbl.message end def test_failing_positive_expectation dbl = double expect(dbl).to receive(:message) end def test_passing_negative_expectation dbl = double expect(dbl).to_not receive(:message) end def test_failing_negative_expectation dbl = double expect(dbl).to_not receive(:message) dbl.message end def test_passing_positive_spy_expectation bond = spy bond.james expect(bond).to have_received(:james) end def test_failing_positive_spy_expectation bond = spy expect(bond).to have_received(:james) end def test_passing_negative_spy_expectation bond = spy expect(bond).not_to have_received(:james) end def test_failing_negative_spy_expectation bond = spy bond.james expect(bond).not_to have_received(:james) end end """ When I run `ruby test/rspec_mocks_test.rb --seed 0` Then it should fail with the following output: | 1) Failure: | | RSpecMocksTest#test_failing_positive_expectation | | (Double (anonymous)).message(*(any args)) | | expected: 1 time with any arguments | | received: 0 times with any arguments | | | | 2) Failure: | | RSpecMocksTest#test_failing_negative_expectation | | (Double (anonymous)).message(no args) | | expected: 0 times with any arguments | | received: 1 time | | | | 3) Failure: | | RSpecMocksTest#test_failing_positive_spy_expectation | | (Double (anonymous)).james(*(any args)) | | expected: 1 time with any arguments | | received: 0 times with any arguments | | | | 4) Failure: | | RSpecMocksTest#test_failing_negative_spy_expectation | | (Double (anonymous)).james(no args) | | expected: 0 times with any arguments | | received: 1 time | | | | 8 runs, 0 assertions, 4 failures, 0 errors, 0 skips | Scenario: Use rspec-mocks with Minitest::Spec Given a file named "spec/rspec_mocks_spec.rb" with: """ruby require 'minitest/autorun' require 'minitest/spec' require 'rspec/mocks/minitest_integration' describe "Minitest Spec integration" do it 'passes a positive expectation' do dbl = double expect(dbl).to receive(:message) dbl.message end it 'fails a positive expectation' do dbl = double expect(dbl).to receive(:message) end it 'passes a negative expectation (using to_not)' do dbl = double expect(dbl).to_not receive(:message) end it 'fails a negative expectation (using not_to)' do dbl = double expect(dbl).not_to receive(:message) dbl.message end end """ When I run `ruby spec/rspec_mocks_spec.rb --seed 0` Then it should fail with the following output: | 1) Failure: | | Minitest Spec integration#test_0002_fails a positive expectation | | (Double (anonymous)).message(*(any args)) | | expected: 1 time with any arguments | | received: 0 times with any arguments | | | | 2) Failure: | | Minitest Spec integration#test_0004_fails a negative expectation (using not_to) | | (Double (anonymous)).message(no args) | | expected: 0 times with any arguments | | received: 1 time | | | | 4 runs, 4 assertions, 2 failures, 0 errors, 0 skips | Scenario: Load rspec-mocks before rspec-expectations, with Minitest::Spec Given a file named "spec/rspec_mocks_spec.rb" with: """ruby require 'minitest/autorun' require 'minitest/spec' require 'rspec/mocks/minitest_integration' require 'rspec/expectations/minitest_integration' describe "Minitest Spec integration" do it 'passes a positive expectation' do dbl = double expect(dbl).to receive(:message) dbl.message end it 'fails a positive expectation' do dbl = double expect(dbl).to receive(:message) end it 'passes a negative expectation (using to_not)' do dbl = double expect(dbl).to_not receive(:message) end it 'fails a negative expectation (using not_to)' do dbl = double expect(dbl).not_to receive(:message) dbl.message end it 'can use both minitest and rspec expectations' do expect(1 + 3).must_equal 4 expect(1 + 3).to eq 4 end end """ When I run `ruby spec/rspec_mocks_spec.rb --seed 0` Then it should fail with the following output: | 1) Failure: | | Minitest Spec integration#test_0002_fails a positive expectation | | (Double (anonymous)).message(*(any args)) | | expected: 1 time with any arguments | | received: 0 times with any arguments | | | | 2) Failure: | | Minitest Spec integration#test_0004_fails a negative expectation (using not_to) | | (Double (anonymous)).message(no args) | | expected: 0 times with any arguments | | received: 1 time | | | | 5 runs, 6 assertions, 2 failures, 0 errors, 0 skips | Scenario: Load rspec-mocks after rspec-expectations, with Minitest::Spec Given a file named "spec/rspec_mocks_spec.rb" with: """ruby require 'minitest/autorun' require 'minitest/spec' require 'rspec/expectations/minitest_integration' require 'rspec/mocks/minitest_integration' describe "Minitest Spec integration" do it 'passes a positive expectation' do dbl = double expect(dbl).to receive(:message) dbl.message end it 'fails a positive expectation' do dbl = double expect(dbl).to receive(:message) end it 'passes a negative expectation (using to_not)' do dbl = double expect(dbl).to_not receive(:message) end it 'fails a negative expectation (using not_to)' do dbl = double expect(dbl).not_to receive(:message) dbl.message end it 'can use both minitest and rspec expectations' do expect(1 + 3).must_equal 4 expect(1 + 3).to eq 4 end end """ When I run `ruby spec/rspec_mocks_spec.rb --seed 0` Then it should fail with the following output: | 1) Failure: | | Minitest Spec integration#test_0002_fails a positive expectation | | (Double (anonymous)).message(*(any args)) | | expected: 1 time with any arguments | | received: 0 times with any arguments | | | | 2) Failure: | | Minitest Spec integration#test_0004_fails a negative expectation (using not_to) | | (Double (anonymous)).message(no args) | | expected: 0 times with any arguments | | received: 1 time | | | | 5 runs, 6 assertions, 2 failures, 0 errors, 0 skips | rspec-mocks-3.13.0/features/outside_rspec/standalone.feature000066400000000000000000000021261455767030500242340ustar00rootroot00000000000000Feature: Using `rspec-mocks` on its own outside of RSpec (standalone mode) `require "rspec/mocks/standalone"` to expose the API at the top level (e.g. `main`) outside the RSpec environment in a REPL like IRB or in a one-off script. Scenario: Allow a message outside RSpec Given a file named "example.rb" with: """ruby require "rspec/mocks/standalone" greeter = double("greeter") allow(greeter).to receive(:say_hi) { "Hello!" } puts greeter.say_hi """ When I run `ruby example.rb` Then the output should contain "Hello!" Scenario: Expect a message outside RSpec Given a file named "example.rb" with: """ruby require "rspec/mocks/standalone" greeter = double("greeter") expect(greeter).to receive(:say_hi) RSpec::Mocks.verify """ When I run `ruby example.rb` Then it should fail with the following output: | (Double "greeter").say_hi(*(any args)) | | RSpec::Mocks::MockExpectationError | | expected: 1 time with any arguments | | received: 0 times with any arguments | rspec-mocks-3.13.0/features/setting_constraints/000077500000000000000000000000001455767030500217625ustar00rootroot00000000000000rspec-mocks-3.13.0/features/setting_constraints/README.md000066400000000000000000000005321455767030500232410ustar00rootroot00000000000000# Setting Constraints RSpec provides a fluent interface off of `expect(...).to receive(...)` that allows you to further constrain what you expect: the arguments, the number of times, and the ordering of multiple messages. Although not shown here, this fluent interface is also supported by [spies](./basics/spies), off of `have_received(...)`. rspec-mocks-3.13.0/features/setting_constraints/matching_arguments.feature000066400000000000000000000252561455767030500272300ustar00rootroot00000000000000Feature: Matching arguments Use `with` to specify the expected arguments. A [message expectation](../basics/expecting-messages) constrained by `with` will only be satisfied when called with matching arguments. A canned response for an [allowed message](../basics/allowing-messages) will only be used when the arguments match. | To match... | ...use an expression like: | ...which matches calls like: | |-----------------------------------------------------|------------------------------------|-----------------------------------------------------| | Literal arguments | `with(1, true)` | `foo(1, true)` | | Literal arguments where one is a hash | `with(1, {x: 1, y: 2})` | `foo(1, x: 1, y: 2) (where last argument is a hash) | | Keyword arguments | `with(x: 1, y: 2)` | `foo(x: 1, y: 2)` (where x and y are keywords) | | Anything that supports case equality (`===`) | `with(/bar/)` | `foo("barn")` | | Any list of args | `with(any_args)` | `foo()`
`foo(1)`
`foo(:bar, 2)` | | Any sublist of args (like an arg splat) | `with(1, any_args)` | `foo(1)`
`foo(1, :bar, :bazz)` | | An empty list of args | `with(no_args)` | `foo()` | | Anything for a given positional arg | `with(3, anything)` | `foo(3, nil)`
`foo(3, :bar)` | | Against an interface | `with(duck_type(:each))` | `foo([])` | | A boolean | `with(3, boolean)` | `foo(3, true)`
`foo(3, false)` | | A subset of a hash | `with(hash_including(:a => 1))` | `foo(:a => 1, :b => 2)` | | An excluded subset of a hash | `with(hash_excluding(:a => 1))` | `foo(:b => 2)` | | A subset of an array | `with(array_including(:a, :b))` | `foo([:a, :b, :c])` | | An excluded subset of an array | `with(array_excluding(:a, :b))` | `foo([:c, :d])` | | An instance of a specific class | `with(instance_of(Integer))` | `foo(3)` | | An object with a given module in its ancestors list | `with(kind_of(Numeric))` | `foo(3)` | | An object with matching attributes | `with(having_attributes(:a => 1))` | `foo(:a => 1, :b => 2)` | | Any RSpec matcher | `with()` | `foo()` | Scenario: Basic example Given a file named "basic_example_spec.rb" with: """ruby RSpec.describe "Constraining a message expectation using with" do let(:dbl) { double } before { expect(dbl).to receive(:foo).with(1, anything, /bar/) } it "passes when the args match" do dbl.foo(1, nil, "barn") end it "fails when the args do not match" do dbl.foo(1, nil, "other") end end """ When I run `rspec basic_example_spec.rb` Then it should fail with the following output: | 2 examples, 1 failure | | | | Failure/Error: dbl.foo(1, nil, "other") | | # received :foo with unexpected arguments | | expected: (1, anything, /bar/) | | got: (1, nil, "other") | @kw-arguments Scenario: Using keyword arguments Given a file named "keyword_example_spec.rb" with: """ruby class WithKeywords def foo(bar: "") end end RSpec.describe "Constraining a message expectation using with" do let(:dbl) { instance_double(WithKeywords) } before { expect(dbl).to receive(:foo).with(bar: "baz") } it "passes when the args match" do dbl.foo(bar: "baz") end it "fails when the args do not match" do dbl.foo(bar: "incorrect") end end """ When I run `rspec keyword_example_spec.rb` Then it should fail with the following output: | 2 examples, 1 failure | | | | Failure/Error: dbl.foo(bar: "incorrect") | | # received :foo with unexpected arguments | | expected: ({:bar=>"baz"}) | | got: ({:bar=>"incorrect"}) | @distincts_kw_args_from_positional_hash Scenario: Using keyword arguments on Rubies that differentiate hashes from keyword arguments Given a file named "keyword_example_spec.rb" with: """ruby class WithKeywords def foo(bar: "") end end RSpec.describe "Constraining a message expectation using with" do let(:dbl) { instance_double(WithKeywords) } before { expect(dbl).to receive(:foo).with(bar: "baz") } it "fails when the args do not match due to a hash" do dbl.foo({bar: "also incorrect"}) end end """ When I run `rspec keyword_example_spec.rb` Then it should fail with the following output: | 1 example, 1 failure | | | | Failure/Error: dbl.foo({bar: "also incorrect"}) | | # received :foo with unexpected arguments | | expected: ({:bar=>"baz"}) (keyword arguments) | | got: ({:bar=>"also incorrect"}) (options hash) | Scenario: Using a RSpec matcher Given a file named "rspec_matcher_spec.rb" with: """ruby RSpec.describe "Using a RSpec matcher" do let(:dbl) { double } before { expect(dbl).to receive(:foo).with(a_collection_containing_exactly(1, 2)) } it "passes when the args match" do dbl.foo([2, 1]) end it "fails when the args do not match" do dbl.foo([1, 3]) end end """ When I run `rspec rspec_matcher_spec.rb` Then it should fail with the following output: | 2 examples, 1 failure | | | | Failure/Error: dbl.foo([1, 3]) | | # received :foo with unexpected arguments | | expected: (a collection containing exactly 1 and 2) | | got: ([1, 3]) | @ripper Scenario: Using satisfy for complex custom expecations Given a file named "rspec_satisfy_spec.rb" with: """ruby RSpec.describe "Using satisfy for complex custom expecations" do let(:dbl) { double } def a_b_c_equals_5 satisfy { |data| data[:a][:b][:c] == 5 } end it "passes when the expectation is true" do expect(dbl).to receive(:foo).with(a_b_c_equals_5) dbl.foo({ :a => { :b => { :c => 5 } } }) end it "fails when the expectation is false" do expect(dbl).to receive(:foo).with(a_b_c_equals_5) dbl.foo({ :a => { :b => { :c => 3 } } }) end end """ When I run `rspec rspec_satisfy_spec.rb` Then it should fail with the following output: | 2 examples, 1 failure | | | | Failure/Error: dbl.foo({ :a => { :b => { :c => 3 } } }) | | # received :foo with unexpected arguments | | expected: (satisfy expression `data[:a][:b][:c] == 5`) | | got: ({:a=>{:b=>{:c=>3}}}) | Scenario: Using a custom matcher Given a file named "custom_matcher_spec.rb" with: """ruby RSpec::Matchers.define :a_multiple_of do |x| match { |actual| (actual % x).zero? } end RSpec.describe "Using a custom matcher" do let(:dbl) { double } before { expect(dbl).to receive(:foo).with(a_multiple_of(3)) } it "passes when the args match" do dbl.foo(12) end it "fails when the args do not match" do dbl.foo(13) end end """ When I run `rspec custom_matcher_spec.rb` Then it should fail with the following output: | 2 examples, 1 failure | | | | Failure/Error: dbl.foo(13) | | # received :foo with unexpected arguments | | expected: (a multiple of 3) | | got: (13) | Scenario: Responding differently based on the arguments Given a file named "responding_differently_spec.rb" with: """ruby RSpec.describe "Using #with to constrain responses" do specify "its response depends on the arguments" do dbl = double # Set a default for any unmatched args allow(dbl).to receive(:foo).and_return(:default) allow(dbl).to receive(:foo).with(1).and_return(1) allow(dbl).to receive(:foo).with(2).and_return(2) expect(dbl.foo(0)).to eq(:default) expect(dbl.foo(1)).to eq(1) expect(dbl.foo(2)).to eq(2) end end """ When I run `rspec responding_differently_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/setting_constraints/message_order.feature000066400000000000000000000054131455767030500261610ustar00rootroot00000000000000Feature: Message Order You can use `ordered` to constrain the order of multiple message expectations. This is not generally recommended because in most situations the order doesn't matter and using `ordered` would make your spec brittle, but it's occasionally useful. When you use `ordered`, the example will only pass if the messages are received in the declared order. Scenario: Passing example Given a file named "passing_example_spec.rb" with: """ruby RSpec.describe "Constraining order" do it "passes when the messages are received in declared order" do collaborator_1 = double("Collaborator 1") collaborator_2 = double("Collaborator 2") expect(collaborator_1).to receive(:step_1).ordered expect(collaborator_2).to receive(:step_2).ordered expect(collaborator_1).to receive(:step_3).ordered collaborator_1.step_1 collaborator_2.step_2 collaborator_1.step_3 end end """ When I run `rspec passing_example_spec.rb` Then the examples should all pass Scenario: Failing examples Given a file named "failing_examples_spec.rb" with: """ruby RSpec.describe "Constraining order" do it "fails when messages are received out of order on one collaborator" do collaborator_1 = double("Collaborator 1") expect(collaborator_1).to receive(:step_1).ordered expect(collaborator_1).to receive(:step_2).ordered collaborator_1.step_2 collaborator_1.step_1 end it "fails when messages are received out of order between collaborators" do collaborator_1 = double("Collaborator 1") collaborator_2 = double("Collaborator 2") expect(collaborator_1).to receive(:step_1).ordered expect(collaborator_2).to receive(:step_2).ordered collaborator_2.step_2 collaborator_1.step_1 end end """ When I run `rspec failing_examples_spec.rb --order defined` Then the examples should all fail, producing the following output: | 1) Constraining order fails when messages are received out of order on one collaborator | | Failure/Error: collaborator_1.step_2 | | # received :step_2 out of order | | | | 2) Constraining order fails when messages are received out of order between collaborators | | Failure/Error: collaborator_2.step_2 | | # received :step_2 out of order | rspec-mocks-3.13.0/features/setting_constraints/receive_counts.feature000066400000000000000000000146741455767030500263700ustar00rootroot00000000000000Feature: Receive Counts When [expecting a message](../basics/expecting-messages), you can specify how many times you expect the message to be received: * `expect(...).to receive(...).once` * `expect(...).to receive(...).twice` * `expect(...).to receive(...).exactly(n).time` * `expect(...).to receive(...).exactly(n).times` * `expect(...).to receive(...).at_least(:once)` * `expect(...).to receive(...).at_least(:twice)` * `expect(...).to receive(...).at_least(n).time` * `expect(...).to receive(...).at_least(n).times` * `expect(...).to receive(...).at_most(:once)` * `expect(...).to receive(...).at_most(:twice)` * `expect(...).to receive(...).at_most(n).time` * `expect(...).to receive(...).at_most(n).times` If you don't specify an expected receive count, it defaults to `once`. Background: Given a file named "lib/account.rb" with: """ruby class Account def initialize(logger) @logger = logger end def open @logger.account_opened end end """ Scenario: Passing examples Given a file named "spec/account_spec.rb" with: """ruby require 'account' RSpec.describe Account do let(:logger) { double("Logger") } let(:account) { Account.new(logger) } example "once" do expect(logger).to receive(:account_opened).once account.open end example "twice" do expect(logger).to receive(:account_opened).twice account.open account.open end example "exactly(n).time" do expect(logger).to receive(:account_opened).exactly(1).time account.open end example "exactly(n).times" do expect(logger).to receive(:account_opened).exactly(3).times account.open account.open account.open end example "at_least(:once)" do expect(logger).to receive(:account_opened).at_least(:once) account.open account.open end example "at_least(:twice)" do expect(logger).to receive(:account_opened).at_least(:twice) account.open account.open account.open end example "at_least(n).time" do expect(logger).to receive(:account_opened).at_least(1).time account.open end example "at_least(n).times" do expect(logger).to receive(:account_opened).at_least(3).times account.open account.open account.open account.open end example "at_most(:once)" do expect(logger).to receive(:account_opened).at_most(:once) end example "at_most(:twice)" do expect(logger).to receive(:account_opened).at_most(:twice) account.open end example "at_most(n).time" do expect(logger).to receive(:account_opened).at_most(1).time account.open end example "at_most(n).times" do expect(logger).to receive(:account_opened).at_most(3).times account.open account.open end end """ When I run `rspec spec/account_spec.rb` Then the examples should all pass Scenario: Failing examples Given a file named "spec/account_spec.rb" with: """ruby require 'account' RSpec.describe Account do let(:logger) { double("Logger") } let(:account) { Account.new(logger) } example "once" do expect(logger).to receive(:account_opened).once account.open account.open end example "twice" do expect(logger).to receive(:account_opened).twice account.open end example "exactly(n).times" do expect(logger).to receive(:account_opened).exactly(3).times account.open account.open end example "at_least(:once)" do expect(logger).to receive(:account_opened).at_least(:once) end example "at_least(:twice)" do expect(logger).to receive(:account_opened).at_least(:twice) account.open end example "at_least(n).times" do expect(logger).to receive(:account_opened).at_least(3).times account.open account.open end example "at_most(:once)" do expect(logger).to receive(:account_opened).at_most(:once) account.open account.open end example "at_most(:twice)" do expect(logger).to receive(:account_opened).at_most(:twice) account.open account.open account.open end example "at_most(n).times" do expect(logger).to receive(:account_opened).at_most(3).times account.open account.open account.open account.open end end """ When I run `rspec spec/account_spec.rb --order defined` Then the examples should all fail, producing the following output: | expected: 1 time with any arguments | | received: 2 times | | | | expected: 2 times with any arguments | | received: 1 time with any arguments | | | | expected: 3 times with any arguments | | received: 2 times with any arguments | | | | expected: at least 1 time with any arguments | | received: 0 times with any arguments | | | | expected: at least 2 times with any arguments | | received: 1 time with any arguments | | | | expected: at least 3 times with any arguments | | received: 2 times with any arguments | | | | expected: at most 1 time with any arguments | | received: 2 times | | | | expected: at most 2 times with any arguments | | received: 3 times | | | | expected: at most 3 times with any arguments | | received: 4 times | rspec-mocks-3.13.0/features/step_definitions/000077500000000000000000000000001455767030500212245ustar00rootroot00000000000000rspec-mocks-3.13.0/features/step_definitions/additional_cli_steps.rb000066400000000000000000000015321455767030500257270ustar00rootroot00000000000000Then /^the example(?:s)? should(?: all)? pass$/ do step %q(the output should contain "0 failures") step %q(the exit status should be 0) end Then /^the examples should all fail, producing the following output:$/ do |table| step %q(the exit status should be 1) examples, failures = all_output.match(/(\d+) examples?, (\d+) failures?/).captures.map(&:to_i) expect(examples).to be > 0 expect(examples).to eq(failures) lines = table.raw.flatten.reject(&:empty?) expect(all_output).to include(*lines) end RSpec::Matchers.define :match_table do |lines| match do |all_output| lines.all? { |line| all_output.include?(line) } end diffable end Then /^it should fail with the following output:$/ do |table| step %q(the exit status should be 1) lines = table.raw.flatten.reject(&:empty?) expect(all_output).to match_table(lines) end rspec-mocks-3.13.0/features/support/000077500000000000000000000000001455767030500173725ustar00rootroot00000000000000rspec-mocks-3.13.0/features/support/disallow_certain_apis.rb000066400000000000000000000013251455767030500242570ustar00rootroot00000000000000# This file is designed to prevent the use of certain APIs that # we don't want used from our cukes, since they function as documentation. if defined?(Cucumber) require 'shellwords' tag = !defined?(::RUBY_ENGINE_VERSION) || (::RUBY_ENGINE_VERSION < '2.0.0') ? '~@allow-old-syntax' : 'not @allow-old-syntax' Before(tag) do set_environment_variable('SPEC_OPTS', "-r#{Shellwords.escape(__FILE__)}") end else module DisallowOneLinerShould def should(*) raise "one-liner should is not allowed" end def should_not(*) raise "one-liner should_not is not allowed" end end RSpec.configure do |rspec| rspec.disable_monkey_patching! rspec.include DisallowOneLinerShould end end rspec-mocks-3.13.0/features/support/env.rb000066400000000000000000000027101455767030500205070ustar00rootroot00000000000000require 'aruba/cucumber' require 'rspec/expectations' Aruba.configure do |config| if RUBY_PLATFORM =~ /java/ || defined?(Rubinius) || (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'truffleruby') config.exit_timeout = 60 else config.exit_timeout = 5 end end Before do if RUBY_PLATFORM == 'java' # disable JIT since these processes are so short lived set_environment_variable('JRUBY_OPTS', "-X-C #{ENV['JRUBY_OPTS']}") end if defined?(Rubinius) # disable JIT since these processes are so short lived set_environment_variable('RBXOPT', "-Xint=true #{ENV['RBXOPT']}") end end Before('@ripper') do |scenario| unless RSpec::Support::RubyFeatures.ripper_supported? warn "Skipping scenario due to lack of Ripper support" if Cucumber::VERSION.to_f >= 3.0 skip_this_scenario else scenario.skip_invoke! end end end Before('@kw-arguments') do |scenario| unless RSpec::Support::RubyFeatures.kw_args_supported? warn "Skipping scenario due to lack of keyword argument support" if Cucumber::VERSION.to_f >= 3.0 skip_this_scenario else scenario.skip_invoke! end end end Before('@distincts_kw_args_from_positional_hash') do |scenario| unless RSpec::Support::RubyFeatures. distincts_kw_args_from_positional_hash? warn "Skipping scenario due to not applicable to this ruby" if Cucumber::VERSION.to_f >= 3.0 skip_this_scenario else scenario.skip_invoke! end end end rspec-mocks-3.13.0/features/support/rubinius.rb000066400000000000000000000003421455767030500215560ustar00rootroot00000000000000# Required until https://github.com/rubinius/rubinius/issues/2430 is resolved ENV['RBXOPT'] = "#{ENV["RBXOPT"]} -Xcompiler.no_rbc" Around "@unsupported-on-rbx" do |_scenario, block| block.call unless defined?(Rubinius) end rspec-mocks-3.13.0/features/verifying_doubles/000077500000000000000000000000001455767030500213755ustar00rootroot00000000000000rspec-mocks-3.13.0/features/verifying_doubles/README.md000066400000000000000000000016351455767030500226610ustar00rootroot00000000000000# Verifying doubles Verifying doubles are a stricter alternative to [normal doubles](./basics/test-doubles) that provide guarantees about what is being verified. When using verifying doubles, RSpec will check that the methods being stubbed are actually present on the underlying object if it is available. Prefer using verifying doubles over normal doubles. No checking will happen if the underlying object or class is not defined, but when run with it present (either as a full spec run or by explicitly preloading collaborators) a failure will be triggered if an invalid method is being stubbed or a method is called with an invalid number of arguments. This dual approach allows you to move very quickly and test components in isolation, while giving you confidence that your doubles are not a complete fiction. Testing in isolation is optional but recommended for classes that do not depend on third-party components. rspec-mocks-3.13.0/features/verifying_doubles/class_doubles.feature000066400000000000000000000042441455767030500256000ustar00rootroot00000000000000Feature: Using a class double `class_double` is provided as a complement to [`instance_double`](./instance-doubles) with the difference that it verifies _class_ methods on the given class rather than instance methods. In addition, it also provides a convenience method `as_stubbed_const` to replace concrete classes with the defined double. See [mutating constants](../mutating-constants) for more details. Note: `class_double` can be used for modules as well. We chose to stick with the `class_double` terminology because the methods a `class_double` verifies against are commonly called "class methods", not "module methods", even when working with a module. Background: Given a file named "lib/user.rb" with: """ruby class User def suspend! ConsoleNotifier.notify("suspended as") end end """ Given a file named "lib/console_notifier.rb" with: """ruby class ConsoleNotifier MAX_WIDTH = 80 def self.notify(message) puts message end end """ Given a file named "spec/user_spec.rb" with: """ruby require 'user' require 'console_notifier' RSpec.describe User, '#suspend!' do it 'notifies the console' do notifier = class_double("ConsoleNotifier"). as_stubbed_const(:transfer_nested_constants => true) expect(notifier).to receive(:notify).with("suspended as") expect(ConsoleNotifier::MAX_WIDTH).to eq(80) user = User.new user.suspend! end end """ Scenario: Replacing existing constants When I run `rspec spec/user_spec.rb` Then the examples should all pass Scenario: Renaming `ConsoleNotifier.notify` to `send_notification` Given a file named "lib/console_notifier.rb" with: """ruby class ConsoleNotifier MAX_WIDTH = 80 def self.send_notification(message) puts message end end """ When I run `rspec spec/user_spec.rb` Then the output should contain "1 example, 1 failure" And the output should contain "the ConsoleNotifier class does not implement the class method:" rspec-mocks-3.13.0/features/verifying_doubles/dynamic_classes.feature000066400000000000000000000063511455767030500261200ustar00rootroot00000000000000Feature: Dynamic classes Verifying instance doubles do not support methods which the class reports to not exist since an actual instance of the class would be required to verify against. This is commonly the case when `method_missing` is used. There are a few ways to work around this. If the object has already been loaded you may consider using an [`object_double`](./object-doubles), but that cannot work if you are testing in isolation. Alternatively you could implement the methods directly (calling `super` to return the `method_missing` definition). Some of these classes may have methods to define these methods on the objects at runtime. (For example, `ActiveRecord` does this to define methods from database columns.) For these cases we provide an API which can be used to customise verifying doubles on creation. We use this ourselves in `rspec-rails` to set up some niceties for you. These types of methods are supported at class level (with `class_double`) however, since `respond_to?` can be queried directly on the class. Background: Given a file named "lib/fake_active_record.rb" with: """ruby class FakeActiveRecord COLUMNS = %w[name email] def respond_to_missing?(method_name) COLUMNS.include?(method_name.to_s) || super end def method_missing(method_name, *args) if respond_to?(method_name) instance_variable_get("@#{method_name}") else super end end def self.define_attribute_methods COLUMNS.each do |name| define_method(name) { instance_variable_get("@#{name}") } end end end """ Given a file named "spec/user_spec.rb" with: """ruby require 'user' RSpec.describe User do it 'can be doubled' do instance_double("User", :name => "Don") end end """ Scenario: Fails with method missing Given a file named "lib/user.rb" with: """ruby require 'fake_active_record' class User < FakeActiveRecord end """ When I run `rspec spec/user_spec.rb` Then the output should contain "1 example, 1 failure" Scenario: Workaround with explicit definitions Given a file named "lib/user.rb" with: """ruby require 'fake_active_record' class User < FakeActiveRecord def name; super end def email; super end end """ When I run `rspec spec/user_spec.rb` Then the examples should all pass Scenario: Workaround using callback Given a file named "lib/user.rb" with: """ruby require 'fake_active_record' class User < FakeActiveRecord end """ And a file named "spec/fake_record_helper.rb" with: """ruby RSpec.configuration.mock_with(:rspec) do |config| config.before_verifying_doubles do |reference| reference.target.define_attribute_methods end end # # or you can use: # # RSpec::Mocks.configuration.before_verifying_doubles do |reference| # reference.target.define_attribute_methods # end """ When I run `rspec -r fake_record_helper spec/user_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/verifying_doubles/instance_doubles.feature000066400000000000000000000065111455767030500262760ustar00rootroot00000000000000Feature: Using an instance double An `instance_double` is the most common type of verifying double. It takes a class name or object as its first argument, then verifies that any methods being stubbed would be present on an _instance_ of that class. In addition, when it receives messages, it verifies that the provided arguments are supported by the method signature, both in terms of arity and allowed or required keyword arguments, if any. The same argument verification happens when you [constrain the arguments](../setting-constraints/matching-arguments) using `with`. For methods handled by `method_missing`, see [dynamic classes](./dynamic-classes). Background: Given a file named "app/models/user.rb" with: """ruby class User < Struct.new(:notifier) def suspend! notifier.notify("suspended as") end end """ Given a file named "spec/unit_helper.rb" with: """ruby $LOAD_PATH.unshift("app/models") """ Given a file named "spec/spec_helper.rb" with: """ruby require 'unit_helper' require 'user' require 'console_notifier' RSpec.configure do |config| config.mock_with :rspec do |mocks| # This option should be set when all dependencies are being loaded # before a spec run, as is the case in a typical spec helper. It will # cause any verifying double instantiation for a class that does not # exist to raise, protecting against incorrectly spelt names. mocks.verify_doubled_constant_names = true end end """ Given a file named "spec/unit/user_spec.rb" with: """ruby require 'unit_helper' require 'user' RSpec.describe User, '#suspend!' do it 'notifies the console' do notifier = instance_double("ConsoleNotifier") expect(notifier).to receive(:notify).with("suspended as") user = User.new(notifier) user.suspend! end end """ Scenario: Spec passes in isolation When I run `rspec spec/unit/user_spec.rb` Then the examples should all pass Scenario: Spec passes with dependencies loaded and method implemented Given a file named "app/models/console_notifier.rb" with: """ruby class ConsoleNotifier def notify(msg) puts message end end """ When I run `rspec -r./spec/spec_helper spec/unit/user_spec.rb` Then the examples should all pass Scenario: Spec fails with dependencies loaded and method unimplemented Given a file named "app/models/console_notifier.rb" with: """ruby class ConsoleNotifier end """ When I run `rspec -r./spec/spec_helper spec/unit/user_spec.rb` Then the output should contain "1 example, 1 failure" And the output should contain "ConsoleNotifier class does not implement the instance method:" Scenario: Spec fails with dependencies loaded and incorrect arity Given a file named "app/models/console_notifier.rb" with: """ruby class ConsoleNotifier def notify(msg, color) puts color + message end end """ When I run `rspec -r./spec/spec_helper spec/unit/user_spec.rb` Then the output should contain "1 example, 1 failure" And the output should contain "Wrong number of arguments." rspec-mocks-3.13.0/features/verifying_doubles/object_doubles.feature000066400000000000000000000042541455767030500257420ustar00rootroot00000000000000Feature: Using an object double `object_double` can be used to create a double from an existing "template" object, from which it verifies that any stubbed methods on the double also exist on the template. This is useful for objects that are readily constructable, but may have far-reaching side-effects such as talking to a database or external API. In this case, using a double rather than the real thing allows you to focus on the communication patterns of the object's interface without having to worry about accidentally causing side-effects. Object doubles can also be used to verify methods defined on an object using `method_missing`, which is not possible with [`instance_double`](./instance-doubles). In addition, `object_double` can be used with specific constant values, as shown below. This is for niche situations, such as when dealing with singleton objects. Scenario: Doubling an existing object Given a file named "spec/user_spec.rb" with: """ruby class User # Don't want to accidentally trigger this! def save; sleep 100; end end def save_user(user) "saved!" if user.save end RSpec.describe '#save_user' do it 'renders message on success' do user = object_double(User.new, :save => true) expect(save_user(user)).to eq("saved!") end end """ When I run `rspec spec/user_spec.rb` Then the examples should all pass Scenario: Doubling a constant object Given a file named "spec/email_spec.rb" with: """ruby require 'logger' module MyApp LOGGER = Logger.new("myapp") end class Email def self.send_to(recipient) MyApp::LOGGER.info("Sent to #{recipient}") # other emailing logic end end RSpec.describe Email do it 'logs a message when sending' do logger = object_double("MyApp::LOGGER", :info => nil).as_stubbed_const Email.send_to('hello@foo.com') expect(logger).to have_received(:info).with("Sent to hello@foo.com") end end """ When I run `rspec spec/email_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/features/verifying_doubles/partial_doubles.feature000066400000000000000000000021401455767030500261200ustar00rootroot00000000000000Feature: Partial doubles When the `verify_partial_doubles` configuration option is set, the same argument and method existence checks that are performed for [`object_double`](./object-doubles) are also performed on [partial doubles](../basics/partial-test-doubles). You should set this unless you have a good reason not to. It defaults to off only for backwards compatibility. Scenario: Doubling an existing object Given a file named "spec/user_spec.rb" with: """ruby class User def save; false; end end def save_user(user) "saved!" if user.save end RSpec.configure do |config| config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end end RSpec.describe '#save_user' do it 'renders message on success' do user = User.new expect(user).to receive(:wave).and_return(true) # Typo in name expect(save_user(user)).to eq("saved!") end end """ When I run `rspec spec/user_spec.rb` Then the output should contain "1 example, 1 failure" rspec-mocks-3.13.0/features/working_with_legacy_code/000077500000000000000000000000001455767030500227075ustar00rootroot00000000000000rspec-mocks-3.13.0/features/working_with_legacy_code/README.md000066400000000000000000000003621455767030500241670ustar00rootroot00000000000000# Working with legacy code RSpec provides a few features that, while not generally recommended, can be useful when you are getting legacy code under test (or in similar situations). Usage of these features should be considered a code smell. rspec-mocks-3.13.0/features/working_with_legacy_code/any_instance.feature000066400000000000000000000141251455767030500267420ustar00rootroot00000000000000Feature: Any Instance rspec-mocks provides two methods, `allow_any_instance_of` and `expect_any_instance_of`, that will allow you to stub or mock any instance of a class. They are used in place of [`allow`](../basics/allowing-messages) or [`expect`](../basics/expecting-messages): ```ruby allow_any_instance_of(Widget).to receive(:name).and_return("Wibble") expect_any_instance_of(Widget).to receive(:name).and_return("Wobble") ``` These methods add the appropriate stub or expectation to all instances of `Widget`. You can also [configure the responses](../configuring-responses) in the same manner. This feature is sometimes useful when working with legacy code, though in general we discourage its use for a number of reasons: * The `rspec-mocks` API is designed for individual object instances, but this feature operates on entire classes of objects. As a result there are some semantically confusing edge cases. For example, in `expect_any_instance_of(Widget).to receive(:name).twice` it isn't clear whether a specific instance is expected to receive `name` twice, or if two receives total are expected. (It's the former.) * Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too complex. * It is the most complicated feature of `rspec-mocks`, and has historically received the most bug reports. (None of the core team actively use it, which doesn't help.) Scenario: Use `allow_any_instance_of` to stub a method Given a file named "example_spec.rb" with: """ruby RSpec.describe "allow_any_instance_of" 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: Use `allow_any_instance_of` to stub multiple methods Given a file named "example_spec.rb" with: """ruby RSpec.describe "allow_any_instance_of" do context "with receive_messages" do it "stubs multiple methods" 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 a class with specific arguments Given a file named "example_spec.rb" with: """ruby RSpec.describe "allow_any_instance_of" 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: Block implementation is passed the receiver as first arg Given a file named "example_spec.rb" with: """ruby RSpec.describe "allow_any_instance_of" do it 'yields the receiver to the block implementation' do allow_any_instance_of(String).to receive(:slice) do |instance, start, length| instance[start, length] end expect('string'.slice(2, 3)).to eq('rin') end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Use `expect_any_instance_of` to set a message expectation on any instance Given a file named "example_spec.rb" with: """ruby RSpec.describe "expect_any_instance_of" do before do expect_any_instance_of(Object).to receive(:foo) end it "passes when an instance receives the message" do Object.new.foo end it "fails when no instance receives the message" do Object.new.to_s end end """ When I run `rspec example_spec.rb` Then it should fail with the following output: | 2 examples, 1 failure | | Exactly one instance should have received the following message(s) but didn't: foo | Scenario: Specify different return values for multiple calls in combination with allow_any_instance_of Using the multiple calls feature with `allow_any_instance_of` results in the behaviour where multiple calls are configured on every instance. Therefore, each individual instance will return the configured return values in the order specified, and then begin to repeat the last value. Given a file named "multiple_calls_spec_with_allow_any_instance_of.rb" with: """ruby class SomeClass end RSpec.describe "When the method is called multiple times on different instances with allow_any_instance_of" do it "demonstrates the mocked behavior on each instance individually" do allow_any_instance_of(SomeClass).to receive(:foo).and_return(1, 2, 3) first = SomeClass.new second = SomeClass.new third = SomeClass.new expect(first.foo).to eq(1) expect(second.foo).to eq(1) expect(first.foo).to eq(2) expect(second.foo).to eq(2) expect(first.foo).to eq(3) expect(first.foo).to eq(3) # repeats last value from here expect(second.foo).to eq(3) expect(second.foo).to eq(3) # repeats last value from here expect(third.foo).to eq(1) expect(third.foo).to eq(2) expect(third.foo).to eq(3) expect(third.foo).to eq(3) # repeats last value from here end end """ When I run `rspec multiple_calls_spec_with_allow_any_instance_of.rb` Then the examples should all pass rspec-mocks-3.13.0/features/working_with_legacy_code/message_chains.feature000066400000000000000000000071451455767030500272440ustar00rootroot00000000000000Feature: Message Chains You can use `receive_message_chain` in place of `receive` in certain circumstances to stub a chain of messages: ```ruby allow(double).to receive_message_chain("foo.bar") { :baz } allow(double).to receive_message_chain(:foo, :bar => :baz) allow(double).to receive_message_chain(:foo, :bar) { :baz } ```` Given any of these three forms: ```ruby double.foo.bar # => :baz ``` Common use in Rails/ActiveRecord: ```ruby allow(Article).to receive_message_chain("recent.published") { [Article.new] } ``` `receive_message_chain` is designed to be used with evaluating a response like `and_return`, `and_yield` etc. For legacy reasons, parity with `stub_chain` is supported but its uses are not considered good practice. Support for `stub_chain` parity may be removed in future versions. Customisations like `exactly` (i.e. `exactly(2).times`) are not supported. Warning: ======== Chains can be arbitrarily long, which makes it quite painless to violate the Law of Demeter in violent ways, so you should consider any use of `receive_message_chain` a code smell. Even though not all code smells indicate real problems (think fluent interfaces), `receive_message_chain` still results in brittle examples. For example, if you write `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the implementation calls `foo.baz.bar`, the stub will not work. Chaining with `receive_message_chain` creates ambiguity in how the chains should be applied and applies design pressure on complex interactions in the implementation code. As such `receive_message_chain` is not a perfect replacement for `receive`. (see [Issue 921](https://github.com/rspec/rspec-mocks/issues/921) for a more detailed explanation). Other mocking methods like `double` and `instance_double` provide a better way of testing code with these interactions. Scenario: Use `receive_message_chain` on a double Given a file named "receive_message_chain_spec.rb" with: """ruby RSpec.describe "Using receive_message_chain on a double" do let(:dbl) { double } example "using a string and a block" do allow(dbl).to receive_message_chain("foo.bar") { :baz } expect(dbl.foo.bar).to eq(:baz) end example "using symbols and a hash" do allow(dbl).to receive_message_chain(:foo, :bar => :baz) expect(dbl.foo.bar).to eq(:baz) end example "using symbols and a block" do allow(dbl).to receive_message_chain(:foo, :bar) { :baz } expect(dbl.foo.bar).to eq(:baz) end end """ When I run `rspec receive_message_chain_spec.rb` Then the examples should all pass Scenario: Use `receive_message_chain` on any instance of a class Given a file named "receive_message_chain_spec.rb" with: """ruby RSpec.describe "Using receive_message_chain on any instance of a class" do example "using a string and a block" do allow_any_instance_of(Object).to receive_message_chain("foo.bar") { :baz } expect(Object.new.foo.bar).to eq(:baz) end example "using symbols and a hash" do allow_any_instance_of(Object).to receive_message_chain(:foo, :bar => :baz) expect(Object.new.foo.bar).to eq(:baz) end example "using symbols and a block" do allow_any_instance_of(Object).to receive_message_chain(:foo, :bar) { :baz } expect(Object.new.foo.bar).to eq(:baz) end end """ When I run `rspec receive_message_chain_spec.rb` Then the examples should all pass rspec-mocks-3.13.0/lib/000077500000000000000000000000001455767030500146065ustar00rootroot00000000000000rspec-mocks-3.13.0/lib/rspec/000077500000000000000000000000001455767030500157225ustar00rootroot00000000000000rspec-mocks-3.13.0/lib/rspec/mocks.rb000066400000000000000000000105121455767030500173620ustar00rootroot00000000000000require 'rspec/support' RSpec::Support.require_rspec_support 'caller_filter' RSpec::Support.require_rspec_support 'warnings' RSpec::Support.require_rspec_support 'ruby_features' RSpec::Support.define_optimized_require_for_rspec(:mocks) { |f| require_relative f } %w[ instance_method_stasher method_double argument_matchers example_methods proxy test_double argument_list_matcher message_expectation order_group error_generator space mutate_const targets syntax configuration verifying_double version ].each { |name| RSpec::Support.require_rspec_mocks name } # Share the top-level RSpec namespace, because we are a core supported # extension. module RSpec # Contains top-level utility methods. While this contains a few # public methods, these are not generally meant to be called from # a test or example. They exist primarily for integration with # test frameworks (such as rspec-core). module Mocks # Performs per-test/example setup. This should be called before # an test or example begins. def self.setup @space_stack << (@space = space.new_scope) end # Verifies any message expectations that were set during the # test or example. This should be called at the end of an example. def self.verify space.verify_all end # Cleans up all test double state (including any methods that were # redefined on partial doubles). This _must_ be called after # each example, even if an error was raised during the example. def self.teardown space.reset_all @space_stack.pop @space = @space_stack.last || @root_space end # Adds an allowance (stub) on `subject` # # @param subject the subject to which the message will be added # @param message a symbol, representing the message that will be # added. # @param opts a hash of options, :expected_from is used to set the # original call site # @yield an optional implementation for the allowance # # @example Defines the implementation of `foo` on `bar`, using the passed block # x = 0 # RSpec::Mocks.allow_message(bar, :foo) { x += 1 } def self.allow_message(subject, message, opts={}, &block) space.proxy_for(subject).add_stub(message, opts, &block) end # Sets a message expectation on `subject`. # @param subject the subject on which the message will be expected # @param message a symbol, representing the message that will be # expected. # @param opts a hash of options, :expected_from is used to set the # original call site # @yield an optional implementation for the expectation # # @example Expect the message `foo` to receive `bar`, then call it # RSpec::Mocks.expect_message(bar, :foo) # bar.foo def self.expect_message(subject, message, opts={}, &block) space.proxy_for(subject).add_message_expectation(message, opts, &block) end # Call the passed block and verify mocks after it has executed. This allows # mock usage in arbitrary places, such as a `before(:all)` hook. # # @return [Object] the return value from the block def self.with_temporary_scope setup begin result = yield verify result ensure teardown end end class << self # @private attr_reader :space end @space_stack = [] @root_space = @space = RSpec::Mocks::RootSpace.new # @private IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored' # To speed up boot time a bit, delay loading optional or rarely # used features until their first use. autoload :AnyInstance, "rspec/mocks/any_instance" autoload :ExpectChain, "rspec/mocks/message_chain" autoload :StubChain, "rspec/mocks/message_chain" autoload :MarshalExtension, "rspec/mocks/marshal_extension" # Namespace for mock-related matchers. module Matchers # @private # just a "tag" for rspec-mock matchers detection module Matcher; end autoload :HaveReceived, "rspec/mocks/matchers/have_received" autoload :Receive, "rspec/mocks/matchers/receive" autoload :ReceiveMessageChain, "rspec/mocks/matchers/receive_message_chain" autoload :ReceiveMessages, "rspec/mocks/matchers/receive_messages" end end end rspec-mocks-3.13.0/lib/rspec/mocks/000077500000000000000000000000001455767030500170365ustar00rootroot00000000000000rspec-mocks-3.13.0/lib/rspec/mocks/any_instance.rb000066400000000000000000000004651455767030500220430ustar00rootroot00000000000000%w[ any_instance/chain any_instance/error_generator any_instance/stub_chain any_instance/stub_chain_chain any_instance/expect_chain_chain any_instance/expectation_chain any_instance/message_chains any_instance/recorder any_instance/proxy ].each { |f| RSpec::Support.require_rspec_mocks(f) } rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/000077500000000000000000000000001455767030500215115ustar00rootroot00000000000000rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/chain.rb000066400000000000000000000056651455767030500231340ustar00rootroot00000000000000module RSpec module Mocks # @private module AnyInstance # @private class Chain def initialize(recorder, *args, &block) @recorder = recorder @expectation_args = args @expectation_block = block @argument_list_matcher = ArgumentListMatcher::MATCH_ALL end # @private # # Provides convenience methods for recording customizations on message # expectations. module Customizations # @macro [attach] record # @method $1(*args, &block) # Records the `$1` message for playback against an instance that # invokes a method stubbed or mocked using `any_instance`. # # @see RSpec::Mocks::MessageExpectation#$1 # def self.record(method_name) define_method(method_name) do |*args, &block| record(method_name, *args, &block) end end record :and_return record :and_raise record :and_throw record :and_yield record :and_call_original record :and_wrap_original record :with record :once record :twice record :thrice record :exactly record :times record :time record :never record :at_least record :at_most end include Customizations # @private def playback!(instance) message_expectation = create_message_expectation_on(instance) messages.inject(message_expectation) do |object, message| object.__send__(*message.first, &message.last) end end # @private def constrained_to_any_of?(*constraints) constraints.any? do |constraint| messages.any? do |message| message.first.first == constraint end end end # @private def matches_args?(*args) @argument_list_matcher.args_match?(*args) end # @private def expectation_fulfilled! @expectation_fulfilled = true end def never AnyInstance.error_generator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated? super end def with(*args, &block) @argument_list_matcher = ArgumentListMatcher.new(*args) super end private def negated? messages.any? { |(message, *_), _| message == :never } end def messages @messages ||= [] end def last_message messages.last.first.first unless messages.empty? end def record(rspec_method_name, *args, &block) verify_invocation_order(rspec_method_name, *args, &block) messages << [args.unshift(rspec_method_name), block] self end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/error_generator.rb000066400000000000000000000022601455767030500252350ustar00rootroot00000000000000module RSpec module Mocks module AnyInstance # @private class ErrorGenerator < ::RSpec::Mocks::ErrorGenerator def raise_second_instance_received_message_error(unfulfilled_expectations) __raise "Exactly one instance should have received the following " \ "message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}" end def raise_does_not_implement_error(klass, method_name) __raise "#{klass} does not implement ##{method_name}" end def raise_message_already_received_by_other_instance_error(method_name, object_inspect, invoked_instance) __raise "The message '#{method_name}' was received by #{object_inspect} " \ "but has already been received by #{invoked_instance}" end def raise_not_supported_with_prepend_error(method_name, problem_mod) __raise "Using `any_instance` to stub a method (#{method_name}) that has been " \ "defined on a prepended module (#{problem_mod}) is not supported." end end def self.error_generator @error_generator ||= ErrorGenerator.new end end end end rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/expect_chain_chain.rb000066400000000000000000000012421455767030500256310ustar00rootroot00000000000000module RSpec module Mocks module AnyInstance # @private class ExpectChainChain < StubChain def initialize(*args) super @expectation_fulfilled = false end def expectation_fulfilled? @expectation_fulfilled end def playback!(instance) super.tap { @expectation_fulfilled = true } end private def create_message_expectation_on(instance) ::RSpec::Mocks::ExpectChain.expect_chain_on(instance, *@expectation_args, &@expectation_block) end def invocation_order EmptyInvocationOrder end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/expectation_chain.rb000066400000000000000000000024151455767030500255250ustar00rootroot00000000000000module RSpec module Mocks module AnyInstance # @private class ExpectationChain < Chain def expectation_fulfilled? @expectation_fulfilled || constrained_to_any_of?(:never) end def initialize(*args, &block) @expectation_fulfilled = false super end private def verify_invocation_order(_rspec_method_name, *_args, &_block) end end # @private class PositiveExpectationChain < ExpectationChain private def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.space.proxy_for(instance) method_name, opts = @expectation_args opts = (opts || {}).merge(:expected_form => IGNORED_BACKTRACE_LINE) me = proxy.add_message_expectation(method_name, opts, &@expectation_block) if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks? me.and_yield_receiver_to_implementation end me end ExpectationInvocationOrder = { :and_return => [:with, nil], :and_raise => [:with, nil], }.freeze def invocation_order ExpectationInvocationOrder end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/message_chains.rb000066400000000000000000000046161455767030500250160ustar00rootroot00000000000000module RSpec module Mocks module AnyInstance # @private class MessageChains def initialize @chains_by_method_name = Hash.new { |h, k| h[k] = [] } end # @private def [](method_name) @chains_by_method_name[method_name] end # @private def add(method_name, chain) @chains_by_method_name[method_name] << chain chain end # @private def remove_stub_chains_for!(method_name) @chains_by_method_name[method_name].reject! do |chain| StubChain === chain end end # @private def has_expectation?(method_name) @chains_by_method_name[method_name].find do |chain| ExpectationChain === chain end end # @private def each_unfulfilled_expectation_matching(method_name, *args) @chains_by_method_name[method_name].each do |chain| yield chain if !chain.expectation_fulfilled? && chain.matches_args?(*args) end end # @private def all_expectations_fulfilled? @chains_by_method_name.all? do |_method_name, chains| chains.all? { |chain| chain.expectation_fulfilled? } end end # @private def unfulfilled_expectations @chains_by_method_name.map do |method_name, chains| method_name.to_s if ExpectationChain === chains.last && !chains.last.expectation_fulfilled? end.compact end # @private def received_expected_message!(method_name) @chains_by_method_name[method_name].each do |chain| chain.expectation_fulfilled! end end # @private def playback!(instance, method_name) raise_if_second_instance_to_receive_message(instance) @chains_by_method_name[method_name].each do |chain| chain.playback!(instance) end end private def raise_if_second_instance_to_receive_message(instance) @instance_with_expectation ||= instance if ExpectationChain === instance return unless ExpectationChain === instance return if @instance_with_expectation.equal?(instance) AnyInstance.error_generator.raise_second_instance_received_message_error(unfulfilled_expectations) end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/proxy.rb000066400000000000000000000112431455767030500232200ustar00rootroot00000000000000module RSpec module Mocks module AnyInstance # @private # The `AnyInstance::Recorder` is responsible for redefining the klass's # instance method in order to add any stubs/expectations the first time # the method is called. It's not capable of updating a stub on an instance # that's already been previously stubbed (either directly, or via # `any_instance`). # # This proxy sits in front of the recorder and delegates both to it # and to the `RSpec::Mocks::Proxy` for each already mocked or stubbed # instance of the class, in order to propagates changes to the instances. # # Note that unlike `RSpec::Mocks::Proxy`, this proxy class is stateless # and is not persisted in `RSpec::Mocks.space`. # # Proxying for the message expectation fluent interface (typically chained # off of the return value of one of these methods) is provided by the # `FluentInterfaceProxy` class below. class Proxy def initialize(recorder, target_proxies) @recorder = recorder @target_proxies = target_proxies end def klass @recorder.klass end def stub(method_name_or_method_map, &block) if Hash === method_name_or_method_map method_name_or_method_map.each do |method_name, return_value| stub(method_name).and_return(return_value) end else perform_proxying(__method__, [method_name_or_method_map], block) do |proxy| proxy.add_stub(method_name_or_method_map, &block) end end end def unstub(method_name) perform_proxying(__method__, [method_name], nil) do |proxy| proxy.remove_stub_if_present(method_name) end end def stub_chain(*chain, &block) perform_proxying(__method__, chain, block) do |proxy| Mocks::StubChain.stub_chain_on(proxy.object, *chain, &block) end end def expect_chain(*chain, &block) perform_proxying(__method__, chain, block) do |proxy| Mocks::ExpectChain.expect_chain_on(proxy.object, *chain, &block) end end def should_receive(method_name, &block) perform_proxying(__method__, [method_name], block) do |proxy| # Yeah, this is a bit odd...but if we used `add_message_expectation` # then it would act like `expect_every_instance_of(klass).to receive`. # The any_instance recorder takes care of validating that an instance # received the message. proxy.add_stub(method_name, &block) end end def should_not_receive(method_name, &block) perform_proxying(__method__, [method_name], block) do |proxy| proxy.add_message_expectation(method_name, &block).never end end private def perform_proxying(method_name, args, block, &target_proxy_block) recorder_value = @recorder.__send__(method_name, *args, &block) proxy_values = @target_proxies.map(&target_proxy_block) FluentInterfaceProxy.new([recorder_value] + proxy_values) end end unless defined?(BasicObject) class BasicObject # Remove all methods except those expected to be defined on BasicObject (instance_methods.map(&:to_sym) - [:__send__, :"!", :instance_eval, :==, :instance_exec, :"!=", :equal?, :__id__, :__binding__, :object_id]).each do |method| undef_method method end end end # @private # Delegates messages to each of the given targets in order to # provide the fluent interface that is available off of message # expectations when dealing with `any_instance`. # # `targets` will typically contain 1 of the `AnyInstance::Recorder` # return values and N `MessageExpectation` instances (one per instance # of the `any_instance` klass). class FluentInterfaceProxy < BasicObject def initialize(targets) @targets = targets end if ::RUBY_VERSION.to_f > 1.8 def respond_to_missing?(method_name, include_private=false) super || @targets.first.respond_to?(method_name, include_private) end else def respond_to?(method_name, include_private=false) super || @targets.first.respond_to?(method_name, include_private) end end def method_missing(*args, &block) return_values = @targets.map { |t| t.__send__(*args, &block) } FluentInterfaceProxy.new(return_values) end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/recorder.rb000066400000000000000000000261141455767030500236470ustar00rootroot00000000000000module RSpec module Mocks module AnyInstance # Given a class `TheClass`, `TheClass.any_instance` returns a `Recorder`, # which records stubs and message expectations for later playback on # instances of `TheClass`. # # Further constraints are stored in instances of [Chain](Chain). # # @see AnyInstance # @see Chain class Recorder # @private attr_reader :message_chains, :stubs, :klass def initialize(klass) @message_chains = MessageChains.new @stubs = Hash.new { |hash, key| hash[key] = [] } @observed_methods = [] @played_methods = {} @backed_up_method_owner = {} @klass = klass @expectation_set = false return unless RSpec::Mocks.configuration.verify_partial_doubles? RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| block.call(ObjectReference.for(klass)) end end # Initializes the recording a stub to be played back against any # instance of this object that invokes the submitted method. # # @see Methods#stub def stub(method_name, &block) observe!(method_name) message_chains.add(method_name, StubChain.new(self, method_name, &block)) end # Initializes the recording a stub chain to be played back against any # instance of this object that invokes the method matching the first # argument. # # @see Methods#stub_chain def stub_chain(*method_names_and_optional_return_values, &block) normalize_chain(*method_names_and_optional_return_values) do |method_name, args| observe!(method_name) message_chains.add(method_name, StubChainChain.new(self, *args, &block)) end end # @private def expect_chain(*method_names_and_optional_return_values, &block) @expectation_set = true normalize_chain(*method_names_and_optional_return_values) do |method_name, args| observe!(method_name) message_chains.add(method_name, ExpectChainChain.new(self, *args, &block)) end end # Initializes the recording a message expectation to be played back # against any instance of this object that invokes the submitted # method. # # @see Methods#should_receive def should_receive(method_name, &block) @expectation_set = true observe!(method_name) message_chains.add(method_name, PositiveExpectationChain.new(self, method_name, &block)) end # The opposite of `should_receive` # # @see Methods#should_not_receive def should_not_receive(method_name, &block) should_receive(method_name, &block).never end # Removes any previously recorded stubs, stub_chains or message # expectations that use `method_name`. # # @see Methods#unstub def unstub(method_name) unless @observed_methods.include?(method_name.to_sym) AnyInstance.error_generator.raise_method_not_stubbed_error(method_name) end message_chains.remove_stub_chains_for!(method_name) stubs[method_name].clear stop_observing!(method_name) unless message_chains.has_expectation?(method_name) end # @api private # # Used internally to verify that message expectations have been # fulfilled. def verify return unless @expectation_set return if message_chains.all_expectations_fulfilled? AnyInstance.error_generator.raise_second_instance_received_message_error(message_chains.unfulfilled_expectations) end # @private def stop_all_observation! @observed_methods.each { |method_name| restore_method!(method_name) } end # @private def playback!(instance, method_name) RSpec::Mocks.space.ensure_registered(instance) message_chains.playback!(instance, method_name) @played_methods[method_name] = instance received_expected_message!(method_name) if message_chains.has_expectation?(method_name) end # @private def instance_that_received(method_name) @played_methods[method_name] end # @private def build_alias_method_name(method_name) "__#{method_name}_without_any_instance__" end # @private def already_observing?(method_name) @observed_methods.include?(method_name) || super_class_observing?(method_name) end # @private def notify_received_message(_object, message, args, _blk) has_expectation = false message_chains.each_unfulfilled_expectation_matching(message, *args) do |expectation| has_expectation = true expectation.expectation_fulfilled! end return unless has_expectation restore_method!(message) mark_invoked!(message) end protected def stop_observing!(method_name) restore_method!(method_name) @observed_methods.delete(method_name) super_class_observers_for(method_name).each do |ancestor| ::RSpec::Mocks.space. any_instance_recorder_for(ancestor).stop_observing!(method_name) end end private def ancestor_is_an_observer?(ancestor, method_name) return if ancestor == @klass ::RSpec::Mocks.space. any_instance_recorder_for(ancestor).already_observing?(method_name) end def super_class_observers_for(method_name) @klass.ancestors.select do |ancestor| ancestor_is_an_observer?(ancestor, method_name) end end def super_class_observing?(method_name) @klass.ancestors.any? do |ancestor| ancestor_is_an_observer?(ancestor, method_name) end end def normalize_chain(*args) args.shift.to_s.split('.').map { |s| s.to_sym }.reverse.each { |a| args.unshift a } yield args.first, args end def received_expected_message!(method_name) message_chains.received_expected_message!(method_name) restore_method!(method_name) mark_invoked!(method_name) end def restore_method!(method_name) if public_protected_or_private_method_defined?(build_alias_method_name(method_name)) restore_original_method!(method_name) else remove_dummy_method!(method_name) end end def restore_original_method!(method_name) return unless @klass.instance_method(method_name).owner == @klass alias_method_name = build_alias_method_name(method_name) @klass.class_exec(@backed_up_method_owner) do |backed_up_method_owner| remove_method method_name # A @klass can have methods implemented (see Method#owner) in @klass # or inherited from a superclass. In ruby 2.2 and earlier, we can copy # a method regardless of the 'owner' and restore it to @klass after # because a call to 'super' from @klass's copied method would end up # calling the original class's superclass's method. # # With the commit below, available starting in 2.3.0, ruby changed # this behavior and a call to 'super' from the method copied to @klass # will call @klass's superclass method, which is the original # implementer of this method! This leads to very strange errors # if @klass's copied method calls 'super', since it would end up # calling itself, the original method implemented in @klass's # superclass. # # For ruby 2.3 and above, we need to only restore methods that # @klass originally owned. # # https://github.com/ruby/ruby/commit/c8854d2ca4be9ee6946e6d17b0e17d9ef130ee81 if RUBY_VERSION < "2.3" || backed_up_method_owner[method_name.to_sym] == self alias_method method_name, alias_method_name end remove_method alias_method_name end end def remove_dummy_method!(method_name) @klass.class_exec do remove_method method_name end end def backup_method!(method_name) return unless public_protected_or_private_method_defined?(method_name) alias_method_name = build_alias_method_name(method_name) @backed_up_method_owner[method_name.to_sym] ||= @klass.instance_method(method_name).owner @klass.class_exec do alias_method alias_method_name, method_name end end def public_protected_or_private_method_defined?(method_name) MethodReference.method_defined_at_any_visibility?(@klass, method_name) end def observe!(method_name) allow_no_prepended_module_definition_of(method_name) if RSpec::Mocks.configuration.verify_partial_doubles? && !Mocks.configuration.temporarily_suppress_partial_double_verification unless public_protected_or_private_method_defined?(method_name) AnyInstance.error_generator.raise_does_not_implement_error(@klass, method_name) end end stop_observing!(method_name) if already_observing?(method_name) @observed_methods << method_name backup_method!(method_name) recorder = self @klass.__send__(:define_method, method_name) do |*args, &blk| recorder.playback!(self, method_name) __send__(method_name, *args, &blk) end @klass.__send__(:ruby2_keywords, method_name) if @klass.respond_to?(:ruby2_keywords, true) end def mark_invoked!(method_name) backup_method!(method_name) recorder = self @klass.__send__(:define_method, method_name) do |*_args, &_blk| invoked_instance = recorder.instance_that_received(method_name) inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>" AnyInstance.error_generator.raise_message_already_received_by_other_instance_error( method_name, inspect, invoked_instance ) end end if Support::RubyFeatures.module_prepends_supported? def allow_no_prepended_module_definition_of(method_name) prepended_modules = RSpec::Mocks::Proxy.prepended_modules_of(@klass) problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) } return unless problem_mod AnyInstance.error_generator.raise_not_supported_with_prepend_error(method_name, problem_mod) end else def allow_no_prepended_module_definition_of(_method_name) # nothing to do; prepends aren't supported on this version of ruby end end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/stub_chain.rb000066400000000000000000000026601455767030500241610ustar00rootroot00000000000000module RSpec module Mocks module AnyInstance # @private class StubChain < Chain # @private def expectation_fulfilled? true end private def create_message_expectation_on(instance) proxy = ::RSpec::Mocks.space.proxy_for(instance) method_name, opts = @expectation_args opts = (opts || {}).merge(:expected_form => IGNORED_BACKTRACE_LINE) stub = proxy.add_stub(method_name, opts, &@expectation_block) @recorder.stubs[stub.message] << stub if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks? stub.and_yield_receiver_to_implementation end stub end InvocationOrder = { :and_return => [:with, nil], :and_raise => [:with, nil], :and_yield => [:with, :and_yield, nil], :and_throw => [:with, nil], :and_call_original => [:with, nil], :and_wrap_original => [:with, nil] }.freeze EmptyInvocationOrder = {}.freeze def invocation_order InvocationOrder end def verify_invocation_order(rspec_method_name, *_args, &_block) return if invocation_order.fetch(rspec_method_name, [nil]).include?(last_message) raise NoMethodError, "Undefined method #{rspec_method_name}" end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/any_instance/stub_chain_chain.rb000066400000000000000000000007501455767030500253210ustar00rootroot00000000000000module RSpec module Mocks module AnyInstance # @private class StubChainChain < StubChain def initialize(*args) super @expectation_fulfilled = false end private def create_message_expectation_on(instance) ::RSpec::Mocks::StubChain.stub_chain_on(instance, *@expectation_args, &@expectation_block) end def invocation_order EmptyInvocationOrder end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/argument_list_matcher.rb000066400000000000000000000112441455767030500237450ustar00rootroot00000000000000# We intentionally do not use the `RSpec::Support.require...` methods # here so that this file can be loaded individually, as documented # below. require 'rspec/mocks/argument_matchers' require 'rspec/support/fuzzy_matcher' module RSpec module Mocks # Wrapper for matching arguments against a list of expected values. Used by # the `with` method on a `MessageExpectation`: # # expect(object).to receive(:message).with(:a, 'b', 3) # object.message(:a, 'b', 3) # # Values passed to `with` can be literal values or argument matchers that # match against the real objects .e.g. # # expect(object).to receive(:message).with(hash_including(:a => 'b')) # # Can also be used directly to match the contents of any `Array`. This # enables 3rd party mocking libs to take advantage of rspec's argument # matching without using the rest of rspec-mocks. # # require 'rspec/mocks/argument_list_matcher' # include RSpec::Mocks::ArgumentMatchers # # arg_list_matcher = RSpec::Mocks::ArgumentListMatcher.new(123, hash_including(:a => 'b')) # arg_list_matcher.args_match?(123, :a => 'b') # # This class is immutable. # # @see ArgumentMatchers class ArgumentListMatcher # @private attr_reader :expected_args # @api public # @param [Array] expected_args a list of expected literals and/or argument matchers # # Initializes an `ArgumentListMatcher` with a collection of literal # values and/or argument matchers. # # @see ArgumentMatchers # @see #args_match? def initialize(*expected_args) @expected_args = expected_args ensure_expected_args_valid! end ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true) # @api public # @param [Array] actual_args # # Matches each element in the `expected_args` against the element in the same # position of the arguments passed to `new`. # # @see #initialize def args_match?(*actual_args) expected_args = resolve_expected_args_based_on(actual_args) return false if expected_args.size != actual_args.size if RUBY_VERSION >= "3" # If the expectation was set with keywords, while the actual method was called with a positional hash argument, they don't match. # If the expectation was set without keywords, e.g., with({a: 1}), then it fine to call it with either foo(a: 1) or foo({a: 1}). # This corresponds to Ruby semantics, as if the method was def foo(options). if Hash === expected_args.last && Hash === actual_args.last if !Hash.ruby2_keywords_hash?(actual_args.last) && Hash.ruby2_keywords_hash?(expected_args.last) return false end end end Support::FuzzyMatcher.values_match?(expected_args, actual_args) end ruby2_keywords :args_match? if respond_to?(:ruby2_keywords, true) # @private # Resolves abstract arg placeholders like `no_args` and `any_args` into # a more concrete arg list based on the provided `actual_args`. def resolve_expected_args_based_on(actual_args) return [] if [ArgumentMatchers::NoArgsMatcher::INSTANCE] == expected_args any_args_index = expected_args.index { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a } return expected_args unless any_args_index replace_any_args_with_splat_of_anything(any_args_index, actual_args.count) end private def replace_any_args_with_splat_of_anything(before_count, actual_args_count) any_args_count = actual_args_count - expected_args.count + 1 after_count = expected_args.count - before_count - 1 any_args = 1.upto(any_args_count).map { ArgumentMatchers::AnyArgMatcher::INSTANCE } expected_args.first(before_count) + any_args + expected_args.last(after_count) end def ensure_expected_args_valid! if expected_args.count { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a } > 1 raise ArgumentError, "`any_args` can only be passed to " \ "`with` once but you have passed it multiple times." elsif expected_args.count > 1 && expected_args.any? { |a| ArgumentMatchers::NoArgsMatcher::INSTANCE == a } raise ArgumentError, "`no_args` can only be passed as a " \ "singleton argument to `with` (i.e. `with(no_args)`), " \ "but you have passed additional arguments." end end # Value that will match all argument lists. # # @private MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher::INSTANCE) end end end rspec-mocks-3.13.0/lib/rspec/mocks/argument_matchers.rb000066400000000000000000000240311455767030500230730ustar00rootroot00000000000000# This cannot take advantage of our relative requires, since this file is a # dependency of `rspec/mocks/argument_list_matcher.rb`. See comment there for # details. require 'rspec/support/matcher_definition' module RSpec module Mocks # ArgumentMatchers are placeholders that you can include in message # expectations to match arguments against a broader check than simple # equality. # # With the exception of `any_args` and `no_args`, they all match against # the arg in same position in the argument list. # # @see ArgumentListMatcher module ArgumentMatchers # Acts like an arg splat, matching any number of args at any point in an arg list. # # @example # expect(object).to receive(:message).with(1, 2, any_args) # # # matches any of these: # object.message(1, 2) # object.message(1, 2, 3) # object.message(1, 2, 3, 4) def any_args AnyArgsMatcher::INSTANCE end # Matches any argument at all. # # @example # expect(object).to receive(:message).with(anything) def anything AnyArgMatcher::INSTANCE end # Matches no arguments. # # @example # expect(object).to receive(:message).with(no_args) def no_args NoArgsMatcher::INSTANCE end # Matches if the actual argument responds to the specified messages. # # @example # expect(object).to receive(:message).with(duck_type(:hello)) # expect(object).to receive(:message).with(duck_type(:hello, :goodbye)) def duck_type(*args) DuckTypeMatcher.new(*args) end # Matches a boolean value. # # @example # expect(object).to receive(:message).with(boolean()) def boolean BooleanMatcher::INSTANCE end # Matches a hash that includes the specified key(s) or key/value pairs. # Ignores any additional keys. # # @example # expect(object).to receive(:message).with(hash_including(:key => val)) # expect(object).to receive(:message).with(hash_including(:key)) # expect(object).to receive(:message).with(hash_including(:key, :key2 => val2)) def hash_including(*args) HashIncludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args)) end # Matches a hash that doesn't include the specified key(s) or key/value. # # @example # expect(object).to receive(:message).with(hash_excluding(:key => val)) # expect(object).to receive(:message).with(hash_excluding(:key)) # expect(object).to receive(:message).with(hash_excluding(:key, :key2 => :val2)) def hash_excluding(*args) HashExcludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args)) end # Matches an array that includes the specified items at least once. # Ignores duplicates and additional values # # @example # expect(object).to receive(:message).with(array_including(1,2,3)) # expect(object).to receive(:message).with(array_including([1,2,3])) def array_including(*args) actually_an_array = Array === args.first && args.count == 1 ? args.first : args ArrayIncludingMatcher.new(actually_an_array) end # Matches an array that excludes the specified items. # # @example # expect(object).to receive(:message).with(array_excluding(1,2,3)) # expect(object).to receive(:message).with(array_excluding([1,2,3])) def array_excluding(*args) actually_an_array = Array === args.first && args.count == 1 ? args.first : args ArrayExcludingMatcher.new(actually_an_array) end alias_method :hash_not_including, :hash_excluding # Matches if `arg.instance_of?(klass)` # # @example # expect(object).to receive(:message).with(instance_of(Thing)) def instance_of(klass) InstanceOf.new(klass) end alias_method :an_instance_of, :instance_of # Matches if `arg.kind_of?(klass)` # # @example # expect(object).to receive(:message).with(kind_of(Thing)) def kind_of(klass) KindOf.new(klass) end alias_method :a_kind_of, :kind_of # @private def self.anythingize_lonely_keys(*args) hash = Hash === args.last ? args.delete_at(-1) : {} args.each { | arg | hash[arg] = AnyArgMatcher::INSTANCE } hash end # Intended to be subclassed by stateless, immutable argument matchers. # Provides a `::INSTANCE` constant for accessing a global # singleton instance of the matcher. There is no need to construct # multiple instance since there is no state. It also facilities the # special case logic we need for some of these matchers, by making it # easy to do comparisons like: `[klass::INSTANCE] == args` rather than # `args.count == 1 && klass === args.first`. # # @private class SingletonMatcher private_class_method :new def self.inherited(subklass) subklass.const_set(:INSTANCE, subklass.send(:new)) end end # @private class AnyArgsMatcher < SingletonMatcher def description "*(any args)" end end # @private class AnyArgMatcher < SingletonMatcher def ===(_other) true end def description "anything" end end # @private class NoArgsMatcher < SingletonMatcher def description "no args" end end # @private class BooleanMatcher < SingletonMatcher def ===(value) true == value || false == value end def description "boolean" end end # @private class BaseHashMatcher def initialize(expected) @expected = expected end def ===(predicate, actual) @expected.__send__(predicate) do |k, v| actual.key?(k) && Support::FuzzyMatcher.values_match?(v, actual[k]) end rescue NoMethodError false end def description(name) "#{name}(#{formatted_expected_hash.inspect.sub(/^\{/, "").sub(/\}$/, "")})" end private def formatted_expected_hash Hash[ @expected.map do |k, v| k = RSpec::Support.rspec_description_for_object(k) v = RSpec::Support.rspec_description_for_object(v) [k, v] end ] end end # @private class HashIncludingMatcher < BaseHashMatcher def ===(actual) super(:all?, actual) end def description super("hash_including") end end # @private class HashExcludingMatcher < BaseHashMatcher def ===(actual) super(:none?, actual) end def description super("hash_not_including") end end # @private class ArrayIncludingMatcher def initialize(expected) @expected = expected end def ===(actual) actual = actual.uniq return true if (actual & @expected).count >= @expected.count @expected.uniq.all? do |expected_element| actual.any? do |actual_element| RSpec::Support::FuzzyMatcher.values_match?(expected_element, actual_element) end end rescue NoMethodError false end def description "array_including(#{formatted_expected_values})" end private def formatted_expected_values @expected.map do |x| RSpec::Support.rspec_description_for_object(x) end.join(", ") end end # @private class ArrayExcludingMatcher def initialize(unexpected) @unexpected = unexpected.uniq end def ===(actual) actual = actual.uniq return false unless (actual & @unexpected).empty? actual.none? do |actual_element| @unexpected.any? do |unexpected_element| RSpec::Support::FuzzyMatcher.values_match?(unexpected_element, actual_element) end end rescue NoMethodError false end def description "array_excluding(#{formatted_unexpected_values})" end private def formatted_unexpected_values @unexpected.map do |x| RSpec::Support.rspec_description_for_object(x) end.join(", ") end end # @private class DuckTypeMatcher def initialize(*methods_to_respond_to) @methods_to_respond_to = methods_to_respond_to end def ===(value) @methods_to_respond_to.all? { |message| value.respond_to?(message) } end def description "duck_type(#{@methods_to_respond_to.map(&:inspect).join(', ')})" end end # @private class InstanceOf def initialize(klass) @klass = klass end def ===(actual) actual.instance_of?(@klass) end def description "an_instance_of(#{@klass.name})" end end # @private class KindOf def initialize(klass) @klass = klass end def ===(actual) actual.kind_of?(@klass) end def description "kind of #{@klass.name}" end end matcher_namespace = name + '::' ::RSpec::Support.register_matcher_definition do |object| # This is the best we have for now. We should tag all of our matchers # with a module or something so we can test for it directly. # # (Note Module#parent in ActiveSupport is defined in a similar way.) begin object.class.name.include?(matcher_namespace) rescue NoMethodError # Some objects, like BasicObject, don't implement standard # reflection methods. false end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/configuration.rb000066400000000000000000000161231455767030500222350ustar00rootroot00000000000000module RSpec module Mocks # Provides configuration options for rspec-mocks. class Configuration def initialize @allow_message_expectations_on_nil = nil @yield_receiver_to_any_instance_implementation_blocks = true @verify_doubled_constant_names = false @transfer_nested_constants = false @verify_partial_doubles = false @temporarily_suppress_partial_double_verification = false @color = false end # Sets whether RSpec will warn, ignore, or fail a test when # expectations are set on nil. # By default, when this flag is not set, warning messages are issued when # expectations are set on nil. This is to prevent false-positives and to # catch potential bugs early on. # When set to `true`, warning messages are suppressed. # When set to `false`, it will raise an error. # # @example # RSpec.configure do |config| # config.mock_with :rspec do |mocks| # mocks.allow_message_expectations_on_nil = false # end # end attr_accessor :allow_message_expectations_on_nil def yield_receiver_to_any_instance_implementation_blocks? @yield_receiver_to_any_instance_implementation_blocks end # Sets whether or not RSpec will yield the receiving instance of a # message to blocks that are used for any_instance stub implementations. # When set, the first yielded argument will be the receiving instance. # Defaults to `true`. # # @example # RSpec.configure do |rspec| # rspec.mock_with :rspec do |mocks| # mocks.yield_receiver_to_any_instance_implementation_blocks = false # end # end attr_writer :yield_receiver_to_any_instance_implementation_blocks # Adds `stub` and `should_receive` to the given # modules or classes. This is usually only necessary # if you application uses some proxy classes that # "strip themselves down" to a bare minimum set of # methods and remove `stub` and `should_receive` in # the process. # # @example # RSpec.configure do |rspec| # rspec.mock_with :rspec do |mocks| # mocks.add_stub_and_should_receive_to Delegator # end # end # def add_stub_and_should_receive_to(*modules) modules.each do |mod| Syntax.enable_should(mod) end end # Provides the ability to set either `expect`, # `should` or both syntaxes. RSpec uses `expect` # syntax by default. This is needed if you want to # explicitly enable `should` syntax and/or explicitly # disable `expect` syntax. # # @example # RSpec.configure do |rspec| # rspec.mock_with :rspec do |mocks| # mocks.syntax = [:expect, :should] # end # end # def syntax=(*values) syntaxes = values.flatten if syntaxes.include?(:expect) Syntax.enable_expect else Syntax.disable_expect end if syntaxes.include?(:should) Syntax.enable_should else Syntax.disable_should end end # Returns an array with a list of syntaxes # that are enabled. # # @example # unless RSpec::Mocks.configuration.syntax.include?(:expect) # raise "this RSpec extension gem requires the rspec-mocks `:expect` syntax" # end # def syntax syntaxes = [] syntaxes << :should if Syntax.should_enabled? syntaxes << :expect if Syntax.expect_enabled? syntaxes end def verify_doubled_constant_names? !!@verify_doubled_constant_names end # When this is set to true, an error will be raised when # `instance_double` or `class_double` is given the name of an undefined # constant. You probably only want to set this when running your entire # test suite, with all production code loaded. Setting this for an # isolated unit test will prevent you from being able to isolate it! attr_writer :verify_doubled_constant_names # Provides a way to perform customisations when verifying doubles. # # @example # RSpec::Mocks.configuration.before_verifying_doubles do |ref| # ref.some_method! # end def before_verifying_doubles(&block) verifying_double_callbacks << block end alias :when_declaring_verifying_double :before_verifying_doubles # @api private # Returns an array of blocks to call when verifying doubles def verifying_double_callbacks @verifying_double_callbacks ||= [] end def transfer_nested_constants? !!@transfer_nested_constants end # Sets the default for the `transfer_nested_constants` option when # stubbing constants. attr_writer :transfer_nested_constants # When set to true, partial mocks will be verified the same as object # doubles. Any stubs will have their arguments checked against the original # method, and methods that do not exist cannot be stubbed. def verify_partial_doubles=(val) @verify_partial_doubles = !!val end def verify_partial_doubles? @verify_partial_doubles end # @private # Used to track wether we are temporarily suppressing verifying partial # doubles with `without_partial_double_verification { ... }` attr_accessor :temporarily_suppress_partial_double_verification if ::RSpec.respond_to?(:configuration) def color? ::RSpec.configuration.color_enabled? end else # Indicates whether or not diffs should be colored. # Delegates to rspec-core's color option if rspec-core # is loaded; otherwise you can set it here. attr_writer :color # Indicates whether or not diffs should be colored. # Delegates to rspec-core's color option if rspec-core # is loaded; otherwise you can set it here. def color? @color end end # Monkey-patch `Marshal.dump` to enable dumping of mocked or stubbed # objects. By default this will not work since RSpec mocks works by # adding singleton methods that cannot be serialized. This patch removes # these singleton methods before serialization. Setting to falsey removes # the patch. # # This method is idempotent. def patch_marshal_to_support_partial_doubles=(val) if val RSpec::Mocks::MarshalExtension.patch! else RSpec::Mocks::MarshalExtension.unpatch! end end # @api private # Resets the configured syntax to the default. def reset_syntaxes_to_default self.syntax = [:should, :expect] RSpec::Mocks::Syntax.warn_about_should! end end # Mocks specific configuration, as distinct from `RSpec.configuration` # which is core RSpec configuration. def self.configuration @configuration ||= Configuration.new end configuration.reset_syntaxes_to_default end end rspec-mocks-3.13.0/lib/rspec/mocks/error_generator.rb000066400000000000000000000347361455767030500225770ustar00rootroot00000000000000RSpec::Support.require_rspec_support "object_formatter" module RSpec module Mocks # Raised when a message expectation is not satisfied. MockExpectationError = Class.new(Exception) # Raised when a test double is used after it has been torn # down (typically at the end of an rspec-core example). ExpiredTestDoubleError = Class.new(MockExpectationError) # Raised when doubles or partial doubles are used outside of the per-test lifecycle. OutsideOfExampleError = Class.new(StandardError) # Raised when an expectation customization method (e.g. `with`, # `and_return`) is called on a message expectation which has already been # invoked. MockExpectationAlreadyInvokedError = Class.new(Exception) # Raised for situations that RSpec cannot support due to mutations made # externally on arguments that RSpec is holding onto to use for later # comparisons. # # @deprecated We no longer raise this error but the constant remains until # RSpec 4 for SemVer reasons. CannotSupportArgMutationsError = Class.new(StandardError) # @private UnsupportedMatcherError = Class.new(StandardError) # @private NegationUnsupportedError = Class.new(StandardError) # @private VerifyingDoubleNotDefinedError = Class.new(StandardError) # @private class ErrorGenerator attr_writer :opts def initialize(target=nil) @target = target end # @private def opts @opts ||= {} end # @private def raise_unexpected_message_error(message, args) __raise "#{intro} received unexpected message :#{message} with #{format_args(args)}" end # @private def raise_unexpected_message_args_error(expectation, args_for_multiple_calls, source_id=nil) __raise error_message(expectation, args_for_multiple_calls), nil, source_id end # @private def raise_missing_default_stub_error(expectation, args_for_multiple_calls) __raise( error_message(expectation, args_for_multiple_calls) + "\n Please stub a default value first if message might be received with other args as well. \n" ) end # @private def raise_similar_message_args_error(expectation, args_for_multiple_calls, backtrace_line=nil) __raise error_message(expectation, args_for_multiple_calls), backtrace_line end def default_error_message(expectation, expected_args, actual_args) "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}".dup end # rubocop:disable Metrics/ParameterLists # @private def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, args, backtrace_line=nil, source_id=nil) expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) received_part = received_part_of_expectation_error(actual_received_count, args) __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}", backtrace_line, source_id end # rubocop:enable Metrics/ParameterLists # @private def raise_unimplemented_error(doubled_module, method_name, object) message = case object when InstanceVerifyingDouble "the %s class does not implement the instance method: %s".dup << if ObjectMethodReference.for(doubled_module, method_name).implemented? ". Perhaps you meant to use `class_double` instead?" else "" end when ClassVerifyingDouble "the %s class does not implement the class method: %s".dup << if InstanceMethodReference.for(doubled_module, method_name).implemented? ". Perhaps you meant to use `instance_double` instead?" else "" end else "%s does not implement: %s" end __raise message % [doubled_module.description, method_name] end # @private def raise_non_public_error(method_name, visibility) raise NoMethodError, "%s method `%s' called on %s" % [ visibility, method_name, intro ] end # @private def raise_invalid_arguments_error(verifier) __raise verifier.error_message end # @private def raise_expired_test_double_error raise ExpiredTestDoubleError, "#{intro} was originally created in one example but has leaked into " \ "another example and can no longer be used. rspec-mocks' doubles are " \ "designed to only last for one example, and you need to create a new " \ "one in each example you wish to use it for." end # @private def describe_expectation(verb, message, expected_received_count, _actual_received_count, args) "#{verb} #{message}#{format_args(args)} #{count_message(expected_received_count)}" end # @private def raise_out_of_order_error(message) __raise "#{intro} received :#{message} out of order" end # @private def raise_missing_block_error(args_to_yield) __raise "#{intro} asked to yield |#{arg_list(args_to_yield)}| but no block was passed" end # @private def raise_wrong_arity_error(args_to_yield, signature) __raise "#{intro} yielded |#{arg_list(args_to_yield)}| to block with #{signature.description}" end # @private def raise_only_valid_on_a_partial_double(method) __raise "#{intro} is a pure test double. `#{method}` is only " \ "available on a partial double." end # @private def raise_expectation_on_unstubbed_method(method) __raise "#{intro} expected to have received #{method}, but that " \ "object is not a spy or method has not been stubbed." end # @private def raise_expectation_on_mocked_method(method) __raise "#{intro} expected to have received #{method}, but that " \ "method has been mocked instead of stubbed or spied." end # @private def raise_double_negation_error(wrapped_expression) __raise "Isn't life confusing enough? You've already set a " \ "negative message expectation and now you are trying to " \ "negate it again with `never`. What does an expression like " \ "`#{wrapped_expression}.not_to receive(:msg).never` even mean?" end # @private def raise_verifying_double_not_defined_error(ref) notify(VerifyingDoubleNotDefinedError.new( "#{ref.description.inspect} is not a defined constant. " \ "Perhaps you misspelt it? " \ "Disable check with `verify_doubled_constant_names` configuration option." )) end # @private def raise_have_received_disallowed(type, reason) __raise "Using #{type}(...) with the `have_received` " \ "matcher is not supported#{reason}." end # @private def raise_cant_constrain_count_for_negated_have_received_error(count_constraint) __raise "can't use #{count_constraint} when negative" end # @private def raise_method_not_stubbed_error(method_name) __raise "The method `#{method_name}` was not stubbed or was already unstubbed" end # @private def raise_already_invoked_error(message, calling_customization) error_message = "The message expectation for #{intro}.#{message} has already been invoked " \ "and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \ "customizations must be applied before it is used for the first time." notify MockExpectationAlreadyInvokedError.new(error_message) end def raise_expectation_on_nil_error(method_name) __raise expectation_on_nil_message(method_name) end def expectation_on_nil_message(method_name) "An expectation of `:#{method_name}` was set on `nil`. " \ "To allow expectations on `nil` and suppress this message, set `RSpec::Mocks.configuration.allow_message_expectations_on_nil` to `true`. " \ "To disallow expectations on `nil`, set `RSpec::Mocks.configuration.allow_message_expectations_on_nil` to `false`" end # @private def intro(unwrapped=false) case @target when TestDouble then TestDoubleFormatter.format(@target, unwrapped) when Class then formatted = "#{@target.inspect} (class)" return formatted if unwrapped "#<#{formatted}>" when NilClass then "nil" else @target.inspect end end # @private def method_call_args_description(args, generic_prefix=" with arguments: ", matcher_prefix=" with ") case args.first when ArgumentMatchers::AnyArgsMatcher then "#{matcher_prefix}any arguments" when ArgumentMatchers::NoArgsMatcher then "#{matcher_prefix}no arguments" else if yield "#{generic_prefix}#{format_args(args)}" else "" end end end private def received_part_of_expectation_error(actual_received_count, args) "received: #{count_message(actual_received_count)}" + method_call_args_description(args) do actual_received_count > 0 && args.length > 0 end end def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) "expected: #{count_message(expected_received_count, expectation_count_type)}" + method_call_args_description(argument_list_matcher.expected_args) do argument_list_matcher.expected_args.length > 0 end end def unexpected_arguments_message(expected_args_string, actual_args_string) "with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}" end def error_message(expectation, args_for_multiple_calls) expected_args = format_args(expectation.expected_args) actual_args = format_received_args(args_for_multiple_calls) if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash? expected_hash = expectation.expected_args.last actual_hash = args_for_multiple_calls.last.last if Hash === expected_hash && Hash === actual_hash && (Hash.ruby2_keywords_hash?(expected_hash) != Hash.ruby2_keywords_hash?(actual_hash)) actual_description = Hash.ruby2_keywords_hash?(actual_hash) ? " (keyword arguments)" : " (options hash)" expected_description = Hash.ruby2_keywords_hash?(expected_hash) ? " (keyword arguments)" : " (options hash)" if actual_description != expected_description actual_args += actual_description expected_args += expected_description end end end message = default_error_message(expectation, expected_args, actual_args) if args_for_multiple_calls.one? diff = diff_message(expectation.expected_args, args_for_multiple_calls.first) if RSpec::Mocks.configuration.color? message << "\nDiff:#{diff}" unless diff.gsub(/\e\[\d+m/, '').strip.empty? else message << "\nDiff:#{diff}" unless diff.strip.empty? end end message end def diff_message(expected_args, actual_args) formatted_expected_args = expected_args.map do |x| RSpec::Support.rspec_description_for_object(x) end formatted_expected_args, actual_args = unpack_string_args(formatted_expected_args, actual_args) differ.diff(actual_args, formatted_expected_args) end def unpack_string_args(formatted_expected_args, actual_args) if [formatted_expected_args, actual_args].all? { |x| list_of_exactly_one_string?(x) } [formatted_expected_args.first, actual_args.first] else [formatted_expected_args, actual_args] end end def list_of_exactly_one_string?(args) Array === args && args.count == 1 && String === args.first end def differ RSpec::Support::Differ.new(:color => RSpec::Mocks.configuration.color?) end def __raise(message, backtrace_line=nil, source_id=nil) message = opts[:message] unless opts[:message].nil? exception = RSpec::Mocks::MockExpectationError.new(message) prepend_to_backtrace(exception, backtrace_line) if backtrace_line notify exception, :source_id => source_id end if RSpec::Support::Ruby.jruby? def prepend_to_backtrace(exception, line) raise exception rescue RSpec::Mocks::MockExpectationError => with_backtrace with_backtrace.backtrace.unshift(line) end else def prepend_to_backtrace(exception, line) exception.set_backtrace(caller.unshift line) end end def notify(*args) RSpec::Support.notify_failure(*args) end def format_args(args) return "(no args)" if args.empty? "(#{arg_list(args)})" end def arg_list(args) args.map { |arg| RSpec::Support::ObjectFormatter.format(arg) }.join(", ") end def format_received_args(args_for_multiple_calls) grouped_args(args_for_multiple_calls).map do |args_for_one_call, index| "#{format_args(args_for_one_call)}#{group_count(index, args_for_multiple_calls)}" end.join("\n ") end def count_message(count, expectation_count_type=nil) return "at least #{times(count.abs)}" if count < 0 || expectation_count_type == :at_least return "at most #{times(count)}" if expectation_count_type == :at_most times(count) end def times(count) "#{count} time#{count == 1 ? '' : 's'}" end def grouped_args(args) Hash[args.group_by { |x| x }.map { |k, v| [k, v.count] }] end def group_count(index, args) " (#{times(index)})" if args.size > 1 || index > 1 end end # @private def self.error_generator @error_generator ||= ErrorGenerator.new end end end rspec-mocks-3.13.0/lib/rspec/mocks/example_methods.rb000066400000000000000000000466111455767030500225510ustar00rootroot00000000000000RSpec::Support.require_rspec_mocks 'object_reference' module RSpec module Mocks # Contains methods intended to be used from within code examples. # Mix this in to your test context (such as a test framework base class) # to use rspec-mocks with your test framework. If you're using rspec-core, # it'll take care of doing this for you. module ExampleMethods include RSpec::Mocks::ArgumentMatchers # @overload double() # @overload double(name) # @param name [String/Symbol] name or description to be used in failure messages # @overload double(stubs) # @param stubs (Hash) hash of message/return-value pairs # @overload double(name, stubs) # @param name [String/Symbol] name or description to be used in failure messages # @param stubs (Hash) hash of message/return-value pairs # @return (Double) # # Constructs an instance of [RSpec::Mocks::Double](RSpec::Mocks::Double) configured # with an optional name, used for reporting in failure messages, and an optional # hash of message/return-value pairs. # # @example # book = double("book", :title => "The RSpec Book") # book.title #=> "The RSpec Book" # # card = double("card", :suit => "Spades", :rank => "A") # card.suit #=> "Spades" # card.rank #=> "A" # def double(*args) ExampleMethods.declare_double(Double, *args) end # @overload instance_double(doubled_class) # @param doubled_class [String, Class] # @overload instance_double(doubled_class, name) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @overload instance_double(doubled_class, stubs) # @param doubled_class [String, Class] # @param stubs [Hash] hash of message/return-value pairs # @overload instance_double(doubled_class, name, stubs) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return InstanceVerifyingDouble # # Constructs a test double against a specific class. If the given class # name has been loaded, only instance methods defined on the class are # allowed to be stubbed. In all other ways it behaves like a # [double](double). def instance_double(doubled_class, *args) ref = ObjectReference.for(doubled_class) ExampleMethods.declare_verifying_double(InstanceVerifyingDouble, ref, *args) end # @overload class_double(doubled_class) # @param doubled_class [String, Module] # @overload class_double(doubled_class, name) # @param doubled_class [String, Module] # @param name [String/Symbol] name or description to be used in failure messages # @overload class_double(doubled_class, stubs) # @param doubled_class [String, Module] # @param stubs [Hash] hash of message/return-value pairs # @overload class_double(doubled_class, name, stubs) # @param doubled_class [String, Module] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return ClassVerifyingDouble # # Constructs a test double against a specific class. If the given class # name has been loaded, only class methods defined on the class are # allowed to be stubbed. In all other ways it behaves like a # [double](double). def class_double(doubled_class, *args) ref = ObjectReference.for(doubled_class) ExampleMethods.declare_verifying_double(ClassVerifyingDouble, ref, *args) end # @overload object_double(object_or_name) # @param object_or_name [String, Object] # @overload object_double(object_or_name, name) # @param object_or_name [String, Object] # @param name [String/Symbol] name or description to be used in failure messages # @overload object_double(object_or_name, stubs) # @param object_or_name [String, Object] # @param stubs [Hash] hash of message/return-value pairs # @overload object_double(object_or_name, name, stubs) # @param object_or_name [String, Object] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return ObjectVerifyingDouble # # Constructs a test double against a specific object. Only the methods # the object responds to are allowed to be stubbed. If a String argument # is provided, it is assumed to reference a constant object which is used # for verification. In all other ways it behaves like a [double](double). def object_double(object_or_name, *args) ref = ObjectReference.for(object_or_name, :allow_direct_object_refs) ExampleMethods.declare_verifying_double(ObjectVerifyingDouble, ref, *args) end # @overload spy() # @overload spy(name) # @param name [String/Symbol] name or description to be used in failure messages # @overload spy(stubs) # @param stubs (Hash) hash of message/return-value pairs # @overload spy(name, stubs) # @param name [String/Symbol] name or description to be used in failure messages # @param stubs (Hash) hash of message/return-value pairs # @return (Double) # # Constructs a test double that is optimized for use with # `have_received`. With a normal double one has to stub methods in order # to be able to spy them. A spy automatically spies on all methods. def spy(*args) double(*args).as_null_object end # @overload instance_spy(doubled_class) # @param doubled_class [String, Class] # @overload instance_spy(doubled_class, name) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @overload instance_spy(doubled_class, stubs) # @param doubled_class [String, Class] # @param stubs [Hash] hash of message/return-value pairs # @overload instance_spy(doubled_class, name, stubs) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return InstanceVerifyingDouble # # Constructs a test double that is optimized for use with `have_received` # against a specific class. If the given class name has been loaded, only # instance methods defined on the class are allowed to be stubbed. With # a normal double one has to stub methods in order to be able to spy # them. An instance_spy automatically spies on all instance methods to # which the class responds. def instance_spy(*args) instance_double(*args).as_null_object end # @overload object_spy(object_or_name) # @param object_or_name [String, Object] # @overload object_spy(object_or_name, name) # @param object_or_name [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @overload object_spy(object_or_name, stubs) # @param object_or_name [String, Object] # @param stubs [Hash] hash of message/return-value pairs # @overload object_spy(object_or_name, name, stubs) # @param object_or_name [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return ObjectVerifyingDouble # # Constructs a test double that is optimized for use with `have_received` # against a specific object. Only instance methods defined on the object # are allowed to be stubbed. With a normal double one has to stub # methods in order to be able to spy them. An object_spy automatically # spies on all methods to which the object responds. def object_spy(*args) object_double(*args).as_null_object end # @overload class_spy(doubled_class) # @param doubled_class [String, Module] # @overload class_spy(doubled_class, name) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @overload class_spy(doubled_class, stubs) # @param doubled_class [String, Module] # @param stubs [Hash] hash of message/return-value pairs # @overload class_spy(doubled_class, name, stubs) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return ClassVerifyingDouble # # Constructs a test double that is optimized for use with `have_received` # against a specific class. If the given class name has been loaded, # only class methods defined on the class are allowed to be stubbed. # With a normal double one has to stub methods in order to be able to spy # them. An class_spy automatically spies on all class methods to which the # class responds. def class_spy(*args) class_double(*args).as_null_object end # Disables warning messages about expectations being set on nil. # # By default warning messages are issued when expectations are set on # nil. This is to prevent false-positives and to catch potential bugs # early on. # @deprecated Use {RSpec::Mocks::Configuration#allow_message_expectations_on_nil} instead. def allow_message_expectations_on_nil RSpec::Mocks.space.proxy_for(nil).warn_about_expectations = false end # Stubs the named constant with the given value. # Like method stubs, the constant will be restored # to its original value (or lack of one, if it was # undefined) when the example completes. # # @param constant_name [String] The fully qualified name of the constant. The current # constant scoping at the point of call is not considered. # @param value [Object] The value to make the constant refer to. When the # example completes, the constant will be restored to its prior state. # @param options [Hash] Stubbing options. # @option options :transfer_nested_constants [Boolean, Array] Determines # what nested constants, if any, will be transferred from the original value # of the constant to the new value of the constant. This only works if both # the original and new values are modules (or classes). # @return [Object] the stubbed value of the constant # # @example # stub_const("MyClass", Class.new) # => Replaces (or defines) MyClass with a new class object. # stub_const("SomeModel::PER_PAGE", 5) # => Sets SomeModel::PER_PAGE to 5. # # class CardDeck # SUITS = [:Spades, :Diamonds, :Clubs, :Hearts] # NUM_CARDS = 52 # end # # stub_const("CardDeck", Class.new) # CardDeck::SUITS # => uninitialized constant error # CardDeck::NUM_CARDS # => uninitialized constant error # # stub_const("CardDeck", Class.new, :transfer_nested_constants => true) # CardDeck::SUITS # => our suits array # CardDeck::NUM_CARDS # => 52 # # stub_const("CardDeck", Class.new, :transfer_nested_constants => [:SUITS]) # CardDeck::SUITS # => our suits array # CardDeck::NUM_CARDS # => uninitialized constant error def stub_const(constant_name, value, options={}) ConstantMutator.stub(constant_name, value, options) end # Hides the named constant with the given value. The constant will be # undefined for the duration of the test. # # Like method stubs, the constant will be restored to its original value # when the example completes. # # @param constant_name [String] The fully qualified name of the constant. # The current constant scoping at the point of call is not considered. # # @example # hide_const("MyClass") # => MyClass is now an undefined constant def hide_const(constant_name) ConstantMutator.hide(constant_name) end # Verifies that the given object received the expected message during the # course of the test. On a spy objects or as null object doubles this # works for any method, on other objects the method must have # been stubbed beforehand in order for messages to be verified. # # Stubbing and verifying messages received in this way implements the # Test Spy pattern. # # @param method_name [Symbol] name of the method expected to have been # called. # # @example # invitation = double('invitation', accept: true) # user.accept_invitation(invitation) # expect(invitation).to have_received(:accept) # # # You can also use most message expectations: # expect(invitation).to have_received(:accept).with(mailer).once # # @note `have_received(...).with(...)` is unable to work properly when # passed arguments are mutated after the spy records the received message. def have_received(method_name, &block) Matchers::HaveReceived.new(method_name, &block) end # Turns off the verifying of partial doubles for the duration of the # block, this is useful in situations where methods are defined at run # time and you wish to define stubs for them but not turn off partial # doubles for the entire run suite. (e.g. view specs in rspec-rails). def without_partial_double_verification original_state = Mocks.configuration.temporarily_suppress_partial_double_verification Mocks.configuration.temporarily_suppress_partial_double_verification = true yield ensure Mocks.configuration.temporarily_suppress_partial_double_verification = original_state end # @method expect # Used to wrap an object in preparation for setting a mock expectation # on it. # # @example # expect(obj).to receive(:foo).with(5).and_return(:return_value) # # @note This method is usually provided by rspec-expectations. However, # if you use rspec-mocks without rspec-expectations, there's a definition # of it that is made available here. If you disable the `:expect` syntax # this method will be undefined. # @method allow # Used to wrap an object in preparation for stubbing a method # on it. # # @example # allow(dbl).to receive(:foo).with(5).and_return(:return_value) # # @note If you disable the `:expect` syntax this method will be undefined. # @method expect_any_instance_of # Used to wrap a class in preparation for setting a mock expectation # on instances of it. # # @example # expect_any_instance_of(MyClass).to receive(:foo) # # @note If you disable the `:expect` syntax this method will be undefined. # @method allow_any_instance_of # Used to wrap a class in preparation for stubbing a method # on instances of it. # # @example # allow_any_instance_of(MyClass).to receive(:foo) # # @note This is only available when you have enabled the `expect` syntax. # @method receive # Used to specify a message that you expect or allow an object # to receive. The object returned by `receive` supports the same # fluent interface that `should_receive` and `stub` have always # supported, allowing you to constrain the arguments or number of # times, and configure how the object should respond to the message. # # @example # expect(obj).to receive(:hello).with("world").exactly(3).times # # @note If you disable the `:expect` syntax this method will be undefined. # @method receive_messages # Shorthand syntax used to setup message(s), and their return value(s), # that you expect or allow an object to receive. The method takes a hash # of messages and their respective return values. Unlike with `receive`, # you cannot apply further customizations using a block or the fluent # interface. # # @example # allow(obj).to receive_messages(:speak => "Hello World") # allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow") # # @note If you disable the `:expect` syntax this method will be undefined. # @method receive_message_chain # @overload receive_message_chain(method1, method2) # @overload receive_message_chain("method1.method2") # @overload receive_message_chain(method1, method_to_value_hash) # # stubs/mocks a chain of messages on an object or test double. # # ## Warning: # # Chains can be arbitrarily long, which makes it quite painless to # violate the Law of Demeter in violent ways, so you should consider any # use of `receive_message_chain` a code smell. Even though not all code smells # indicate real problems (think fluent interfaces), `receive_message_chain` still # results in brittle examples. For example, if you write # `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the # implementation calls `foo.baz.bar`, the stub will not work. # # @example # allow(double).to receive_message_chain("foo.bar") { :baz } # allow(double).to receive_message_chain(:foo, :bar => :baz) # allow(double).to receive_message_chain(:foo, :bar) { :baz } # # # Given any of ^^ these three forms ^^: # double.foo.bar # => :baz # # # Common use in Rails/ActiveRecord: # allow(Article).to receive_message_chain("recent.published") { [Article.new] } # # @note If you disable the `:expect` syntax this method will be undefined. # @private def self.included(klass) klass.class_exec do # This gets mixed in so that if `RSpec::Matchers` is included in # `klass` later, its definition of `expect` will take precedence. include ExpectHost unless method_defined?(:expect) end end # @private def self.extended(object) # This gets extended in so that if `RSpec::Matchers` is included in # `klass` later, its definition of `expect` will take precedence. object.extend ExpectHost unless object.respond_to?(:expect) end # @private def self.declare_verifying_double(type, ref, *args) if RSpec::Mocks.configuration.verify_doubled_constant_names? && !ref.defined? RSpec::Mocks.error_generator.raise_verifying_double_not_defined_error(ref) end RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| block.call(ref) end declare_double(type, ref, *args) end # @private def self.declare_double(type, *args) args << {} unless Hash === args.last type.new(*args) end # This module exists to host the `expect` method for cases where # rspec-mocks is used w/o rspec-expectations. module ExpectHost end end end end rspec-mocks-3.13.0/lib/rspec/mocks/instance_method_stasher.rb000066400000000000000000000110351455767030500242600ustar00rootroot00000000000000module RSpec module Mocks # @private class InstanceMethodStasher def initialize(object, method) @object = object @method = method @klass = (class << object; self; end) @original_method = nil @method_is_stashed = false end attr_reader :original_method if RUBY_VERSION.to_f < 1.9 # @private def method_is_stashed? @method_is_stashed end # @private def stash return if !method_defined_directly_on_klass? || @method_is_stashed @klass.__send__(:alias_method, stashed_method_name, @method) @method_is_stashed = true end # @private def stashed_method_name "obfuscated_by_rspec_mocks__#{@method}" end # @private def restore return unless @method_is_stashed if @klass.__send__(:method_defined?, @method) @klass.__send__(:undef_method, @method) end @klass.__send__(:alias_method, @method, stashed_method_name) @klass.__send__(:remove_method, stashed_method_name) @method_is_stashed = false end else # @private def method_is_stashed? !!@original_method end # @private def stash return unless method_defined_directly_on_klass? @original_method ||= ::RSpec::Support.method_handle_for(@object, @method) @klass.__send__(:undef_method, @method) end # @private def restore return unless @original_method if @klass.__send__(:method_defined?, @method) @klass.__send__(:undef_method, @method) end handle_restoration_failures do @klass.__send__(:define_method, @method, @original_method) end @original_method = nil end end if RUBY_DESCRIPTION.include?('2.0.0p247') || RUBY_DESCRIPTION.include?('2.0.0p195') # ruby 2.0.0-p247 and 2.0.0-p195 both have a bug that we can't work around :(. # https://bugs.ruby-lang.org/issues/8686 def handle_restoration_failures yield rescue TypeError RSpec.warn_with( "RSpec failed to properly restore a partial double (#{@object.inspect}) " \ "to its original state due to a known bug in MRI 2.0.0-p195 & p247 " \ "(https://bugs.ruby-lang.org/issues/8686). This object may remain " \ "screwed up for the rest of this process. Please upgrade to 2.0.0-p353 or above.", :call_site => nil, :use_spec_location_as_call_site => true ) end else def handle_restoration_failures # No known reasons for restoration to fail on other rubies. yield end end private # @private def method_defined_directly_on_klass? method_defined_on_klass? && method_owned_by_klass? end # @private def method_defined_on_klass?(klass=@klass) MethodReference.method_defined_at_any_visibility?(klass, @method) end def method_owned_by_klass? owner = @klass.instance_method(@method).owner # On Ruby 2.0.0+ the owner of a method on a class which has been # `prepend`ed may actually be an instance, e.g. # `#`, rather than the expected `MyClass`. owner = owner.class unless Module === owner # On some 1.9s (e.g. rubinius) aliased methods # can report the wrong owner. Example: # class MyClass # class << self # alias alternate_new new # end # end # # MyClass.owner(:alternate_new) returns `Class` when incorrect, # but we need to consider the owner to be `MyClass` because # it is not actually available on `Class` but is on `MyClass`. # Hence, we verify that the owner actually has the method defined. # If the given owner does not have the method defined, we assume # that the method is actually owned by @klass. # # On 1.8, aliased methods can also report the wrong owner. Example: # module M # def a; end # module_function :a # alias b a # module_function :b # end # The owner of M.b is the raw Module object, instead of the expected # singleton class of the module return true if RUBY_VERSION < '1.9' && owner == @object owner == @klass || !(method_defined_on_klass?(owner)) end end end end rspec-mocks-3.13.0/lib/rspec/mocks/marshal_extension.rb000066400000000000000000000022221455767030500231040ustar00rootroot00000000000000module RSpec module Mocks # Support for `patch_marshal_to_support_partial_doubles` configuration. # # @private class MarshalExtension def self.patch! return if Marshal.respond_to?(:dump_with_rspec_mocks) Marshal.instance_eval do class << self def dump_with_rspec_mocks(object, *rest) if !::RSpec::Mocks.space.registered?(object) || NilClass === object dump_without_rspec_mocks(object, *rest) else dump_without_rspec_mocks(object.dup, *rest) end end alias_method :dump_without_rspec_mocks, :dump undef_method :dump alias_method :dump, :dump_with_rspec_mocks end end end def self.unpatch! return unless Marshal.respond_to?(:dump_with_rspec_mocks) Marshal.instance_eval do class << self undef_method :dump_with_rspec_mocks undef_method :dump alias_method :dump, :dump_without_rspec_mocks undef_method :dump_without_rspec_mocks end end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/matchers/000077500000000000000000000000001455767030500206445ustar00rootroot00000000000000rspec-mocks-3.13.0/lib/rspec/mocks/matchers/expectation_customization.rb000066400000000000000000000006611455767030500265070ustar00rootroot00000000000000module RSpec module Mocks module Matchers # @private class ExpectationCustomization attr_accessor :block def initialize(method_name, args, block) @method_name = method_name @args = args @block = block end def playback_onto(expectation) expectation.__send__(@method_name, *@args, &@block) end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/matchers/have_received.rb000066400000000000000000000073101455767030500237630ustar00rootroot00000000000000module RSpec module Mocks module Matchers # @private class HaveReceived include Matcher COUNT_CONSTRAINTS = %w[exactly at_least at_most times time once twice thrice] ARGS_CONSTRAINTS = %w[with] CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w[ordered] def initialize(method_name, &block) @method_name = method_name @block = block @constraints = [] @subject = nil end def matcher_name "have_received" end def matches?(subject, &block) @block ||= block @subject = subject @expectation = expect mock_proxy.ensure_implemented(@method_name) expected_messages_received_in_order? end def does_not_match?(subject) @subject = subject ensure_count_unconstrained @expectation = expect.never mock_proxy.ensure_implemented(@method_name) expected_messages_received_in_order? end def failure_message capture_failure_message end def failure_message_when_negated capture_failure_message end def description (@expectation ||= expect).description_for("have received") end CONSTRAINTS.each do |expectation| define_method expectation do |*args| @constraints << [expectation, *args] self end end def setup_expectation(subject, &block) notify_failure_message unless matches?(subject, &block) end def setup_negative_expectation(subject, &block) notify_failure_message unless does_not_match?(subject, &block) end def setup_allowance(_subject, &_block) disallow("allow", " as it would have no effect") end def setup_any_instance_allowance(_subject, &_block) disallow("allow_any_instance_of") end def setup_any_instance_expectation(_subject, &_block) disallow("expect_any_instance_of") end def setup_any_instance_negative_expectation(_subject, &_block) disallow("expect_any_instance_of") end private def disallow(type, reason="") RSpec::Mocks.error_generator.raise_have_received_disallowed(type, reason) end def expect expectation = mock_proxy.build_expectation(@method_name) apply_constraints_to expectation expectation end def apply_constraints_to(expectation) @constraints.each do |constraint| expectation.send(*constraint) end end def ensure_count_unconstrained return unless count_constraint RSpec::Mocks.error_generator.raise_cant_constrain_count_for_negated_have_received_error(count_constraint) end def count_constraint @constraints.map(&:first).find do |constraint| COUNT_CONSTRAINTS.include?(constraint) end end def capture_failure_message RSpec::Support.with_failure_notifier(Proc.new { |err, _opt| return err.message }) do notify_failure_message end end def notify_failure_message mock_proxy.check_for_unexpected_arguments(@expectation) @expectation.generate_error end def expected_messages_received_in_order? mock_proxy.replay_received_message_on @expectation, &@block @expectation.expected_messages_received? && @expectation.ensure_expected_ordering_received! end def mock_proxy RSpec::Mocks.space.proxy_for(@subject) end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/matchers/receive.rb000066400000000000000000000106041455767030500226140ustar00rootroot00000000000000RSpec::Support.require_rspec_mocks 'matchers/expectation_customization' module RSpec module Mocks module Matchers # @private class Receive include Matcher def initialize(message, block) @message = message @block = block @recorded_customizations = [] end def matcher_name "receive" end def description describable.description_for("receive") end def setup_expectation(subject, &block) warn_if_any_instance("expect", subject) @describable = setup_mock_proxy_method_substitute(subject, :add_message_expectation, block) end alias matches? setup_expectation def setup_negative_expectation(subject, &block) # ensure `never` goes first for cases like `never.and_return(5)`, # where `and_return` is meant to raise an error @recorded_customizations.unshift ExpectationCustomization.new(:never, [], nil) warn_if_any_instance("expect", subject) setup_expectation(subject, &block) end alias does_not_match? setup_negative_expectation def setup_allowance(subject, &block) warn_if_any_instance("allow", subject) setup_mock_proxy_method_substitute(subject, :add_stub, block) end def setup_any_instance_expectation(subject, &block) setup_any_instance_method_substitute(subject, :should_receive, block) end def setup_any_instance_negative_expectation(subject, &block) setup_any_instance_method_substitute(subject, :should_not_receive, block) end def setup_any_instance_allowance(subject, &block) setup_any_instance_method_substitute(subject, :stub, block) end own_methods = (instance_methods - superclass.instance_methods) MessageExpectation.public_instance_methods(false).each do |method| next if own_methods.include?(method) define_method(method) do |*args, &block| @recorded_customizations << ExpectationCustomization.new(method, args, block) self end ruby2_keywords(method) if respond_to?(:ruby2_keywords, true) end private def describable @describable ||= DefaultDescribable.new(@message) end def warn_if_any_instance(expression, subject) return unless AnyInstance::Proxy === subject RSpec.warning( "`#{expression}(#{subject.klass}.any_instance).to` " \ "is probably not what you meant, it does not operate on " \ "any instance of `#{subject.klass}`. " \ "Use `#{expression}_any_instance_of(#{subject.klass}).to` instead." ) end def setup_mock_proxy_method_substitute(subject, method, block) proxy = ::RSpec::Mocks.space.proxy_for(subject) setup_method_substitute(proxy, method, block) end def setup_any_instance_method_substitute(subject, method, block) proxy = ::RSpec::Mocks.space.any_instance_proxy_for(subject) setup_method_substitute(proxy, method, block) end def setup_method_substitute(host, method, block, *args) args << @message.to_sym block = move_block_to_last_customization(block) expectation = host.__send__(method, *args, &(@block || block)) @recorded_customizations.each do |customization| customization.playback_onto(expectation) end expectation end def move_block_to_last_customization(block) last = @recorded_customizations.last return block unless last last.block ||= block nil end # MessageExpectation objects are able to describe themselves in detail. # We use this as a fall back when a MessageExpectation is not available. # @private class DefaultDescribable def initialize(message) @message = message end # This is much simpler for the `any_instance` case than what the # user may want, but I'm not up for putting a bunch of effort # into full descriptions for `any_instance` expectations at this point :(. def description_for(verb) "#{verb} #{@message}" end end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/matchers/receive_message_chain.rb000066400000000000000000000046331455767030500254670ustar00rootroot00000000000000RSpec::Support.require_rspec_mocks 'matchers/expectation_customization' module RSpec module Mocks module Matchers # @private class ReceiveMessageChain include Matcher def initialize(chain, &block) @chain = chain @block = block @recorded_customizations = [] end [:with, :and_return, :and_invoke, :and_throw, :and_raise, :and_yield, :and_call_original].each do |msg| define_method(msg) do |*args, &block| @recorded_customizations << ExpectationCustomization.new(msg, args, block) self end end def matcher_name "receive_message_chain" end def description "receive message chain #{formatted_chain}" end def setup_allowance(subject, &block) chain = StubChain.stub_chain_on(subject, *@chain, &(@block || block)) replay_customizations(chain) end def setup_any_instance_allowance(subject, &block) proxy = ::RSpec::Mocks.space.any_instance_proxy_for(subject) chain = proxy.stub_chain(*@chain, &(@block || block)) replay_customizations(chain) end def setup_any_instance_expectation(subject, &block) proxy = ::RSpec::Mocks.space.any_instance_proxy_for(subject) chain = proxy.expect_chain(*@chain, &(@block || block)) replay_customizations(chain) end def setup_expectation(subject, &block) chain = ExpectChain.expect_chain_on(subject, *@chain, &(@block || block)) replay_customizations(chain) end def setup_negative_expectation(*_args) raise NegationUnsupportedError, "`expect(...).not_to receive_message_chain` is not supported " \ "since it doesn't really make sense. What would it even mean?" end alias matches? setup_expectation alias does_not_match? setup_negative_expectation private def replay_customizations(chain) @recorded_customizations.each do |customization| customization.playback_onto(chain) end end def formatted_chain @formatted_chain ||= @chain.map do |part| if Hash === part part.keys.first.to_s else part.to_s end end.join(".") end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/matchers/receive_messages.rb000066400000000000000000000044251455767030500245070ustar00rootroot00000000000000module RSpec module Mocks module Matchers # @private class ReceiveMessages include Matcher def initialize(message_return_value_hash) @message_return_value_hash = message_return_value_hash @backtrace_line = CallerFilter.first_non_rspec_line end def matcher_name "receive_messages" end def description "receive messages: #{@message_return_value_hash.inspect}" end def setup_expectation(subject) warn_about_block if block_given? each_message_on(proxy_on(subject)) do |host, message, return_value| host.add_simple_expectation(message, return_value, @backtrace_line) end end alias matches? setup_expectation def setup_negative_expectation(_subject) raise NegationUnsupportedError, "`expect(...).to_not receive_messages` is not supported since it " \ "doesn't really make sense. What would it even mean?" end alias does_not_match? setup_negative_expectation def setup_allowance(subject) warn_about_block if block_given? each_message_on(proxy_on(subject)) do |host, message, return_value| host.add_simple_stub(message, return_value) end end def setup_any_instance_expectation(subject) warn_about_block if block_given? each_message_on(any_instance_of(subject)) do |host, message, return_value| host.should_receive(message).and_return(return_value) end end def setup_any_instance_allowance(subject) warn_about_block if block_given? any_instance_of(subject).stub(@message_return_value_hash) end def warn_about_block raise "Implementation blocks aren't supported with `receive_messages`" end private def proxy_on(subject) ::RSpec::Mocks.space.proxy_for(subject) end def any_instance_of(subject) ::RSpec::Mocks.space.any_instance_proxy_for(subject) end def each_message_on(host) @message_return_value_hash.each do |message, value| yield host, message, value end end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/message_chain.rb000066400000000000000000000044061455767030500221550ustar00rootroot00000000000000module RSpec module Mocks # @private class MessageChain attr_reader :object, :chain, :block def initialize(object, *chain, &blk) @object = object @chain, @block = format_chain(*chain, &blk) end # @api private def setup_chain if chain.length > 1 if (matching_stub = find_matching_stub) chain.shift chain_on(matching_stub.invoke(nil), *chain, &@block) elsif (matching_expectation = find_matching_expectation) chain.shift chain_on(matching_expectation.invoke_without_incrementing_received_count(nil), *chain, &@block) else next_in_chain = Double.new expectation(object, chain.shift) { next_in_chain } chain_on(next_in_chain, *chain, &@block) end else expectation(object, chain.shift, &@block) end end private def chain_on(object, *chain, &block) initialize(object, *chain, &block) setup_chain end def format_chain(*chain, &blk) if Hash === chain.last hash = chain.pop hash.each do |k, v| chain << k blk = Proc.new { v } end end return chain.join('.').split('.'), blk end def find_matching_stub ::RSpec::Mocks.space.proxy_for(object). __send__(:find_matching_method_stub, chain.first.to_sym) end def find_matching_expectation ::RSpec::Mocks.space.proxy_for(object). __send__(:find_matching_expectation, chain.first.to_sym) end end # @private class ExpectChain < MessageChain # @api private def self.expect_chain_on(object, *chain, &blk) new(object, *chain, &blk).setup_chain end private def expectation(object, message, &return_block) ::RSpec::Mocks.expect_message(object, message, {}, &return_block) end end # @private class StubChain < MessageChain def self.stub_chain_on(object, *chain, &blk) new(object, *chain, &blk).setup_chain end private def expectation(object, message, &return_block) ::RSpec::Mocks.allow_message(object, message, {}, &return_block) end end end end rspec-mocks-3.13.0/lib/rspec/mocks/message_expectation.rb000066400000000000000000000676161455767030500234320ustar00rootroot00000000000000RSpec::Support.require_rspec_support 'mutex' module RSpec module Mocks # A message expectation that only allows concrete return values to be set # for a message. While this same effect can be achieved using a standard # MessageExpectation, this version is much faster and so can be used as an # optimization. # # @private class SimpleMessageExpectation def initialize(message, response, error_generator, backtrace_line=nil) @message, @response, @error_generator, @backtrace_line = message.to_sym, response, error_generator, backtrace_line @received = false end def invoke(*_) @received = true @response end def matches?(message, *_) @message == message.to_sym end def called_max_times? false end def verify_messages_received return if @received @error_generator.raise_expectation_error( @message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, [], @backtrace_line ) end def unadvise(_) end end # Represents an individual method stub or message expectation. The methods # defined here can be used to configure how it behaves. The methods return # `self` so that they can be chained together to form a fluent interface. class MessageExpectation # @!group Configuring Responses # @overload and_return(value) # @overload and_return(first_value, second_value) # # Tells the object to return a value when it receives the message. Given # more than one value, the first value is returned the first time the # message is received, the second value is returned the next time, etc, # etc. # # If the message is received more times than there are values, the last # value is returned for every subsequent call. # # @return [nil] No further chaining is supported after this. # @example # allow(counter).to receive(:count).and_return(1) # counter.count # => 1 # counter.count # => 1 # # allow(counter).to receive(:count).and_return(1,2,3) # counter.count # => 1 # counter.count # => 2 # counter.count # => 3 # counter.count # => 3 # counter.count # => 3 # # etc def and_return(first_value, *values) raise_already_invoked_error_if_necessary(__method__) if negative? raise "`and_return` is not supported with negative message expectations" end if block_given? raise ArgumentError, "Implementation blocks aren't supported with `and_return`" end values.unshift(first_value) @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least) self.terminal_implementation_action = AndReturnImplementation.new(values) nil end # Tells the object to invoke a Proc when it receives the message. Given # more than one value, the result of the first Proc is returned the first # time the message is received, the result of the second Proc is returned # the next time, etc, etc. # # If the message is received more times than there are Procs, the result of # the last Proc is returned for every subsequent call. # # @return [nil] No further chaining is supported after this. # @example # allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout }) # api.get_foo # => raises ApiTimeout # api.get_foo # => raises ApiTimeout # # allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout }, -> { raise ApiTimeout }, -> { :a_foo }) # api.get_foo # => raises ApiTimeout # api.get_foo # => rasies ApiTimeout # api.get_foo # => :a_foo # api.get_foo # => :a_foo # api.get_foo # => :a_foo # # etc def and_invoke(first_proc, *procs) raise_already_invoked_error_if_necessary(__method__) if negative? raise "`and_invoke` is not supported with negative message expectations" end if block_given? raise ArgumentError, "Implementation blocks aren't supported with `and_invoke`" end procs.unshift(first_proc) if procs.any? { |p| !p.respond_to?(:call) } raise ArgumentError, "Arguments to `and_invoke` must be callable." end @expected_received_count = [@expected_received_count, procs.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least) self.terminal_implementation_action = AndInvokeImplementation.new(procs) nil end # Tells the object to delegate to the original unmodified method # when it receives the message. # # @note This is only available on partial doubles. # # @return [nil] No further chaining is supported after this. # @example # expect(counter).to receive(:increment).and_call_original # original_count = counter.count # counter.increment # expect(counter.count).to eq(original_count + 1) def and_call_original block = lambda do |original, *args, &b| original.call(*args, &b) end block = block.ruby2_keywords if block.respond_to?(:ruby2_keywords) wrap_original(__method__, &block) end # Decorates the stubbed method with the supplied block. The original # unmodified method is passed to the block along with any method call # arguments so you can delegate to it, whilst still being able to # change what args are passed to it and/or change the return value. # # @note This is only available on partial doubles. # # @return [nil] No further chaining is supported after this. # @example # expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block| # original_method.call(*args, &block).first(10) # end def and_wrap_original(&block) wrap_original(__method__, &block) end # @overload and_raise # @overload and_raise(ExceptionClass) # @overload and_raise(ExceptionClass, message) # @overload and_raise(exception_instance) # # Tells the object to raise an exception when the message is received. # # @return [nil] No further chaining is supported after this. # @note # When you pass an exception class, the MessageExpectation will raise # an instance of it, creating it with `exception` and passing `message` # if specified. If the exception class initializer requires more than # one parameters, you must pass in an instance and not the class, # otherwise this method will raise an ArgumentError exception. # # @example # allow(car).to receive(:go).and_raise # allow(car).to receive(:go).and_raise(OutOfGas) # allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive") # allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz)) def and_raise(*args) raise_already_invoked_error_if_necessary(__method__) self.terminal_implementation_action = Proc.new { raise(*args) } nil end # @overload and_throw(symbol) # @overload and_throw(symbol, object) # # Tells the object to throw a symbol (with the object if that form is # used) when the message is received. # # @return [nil] No further chaining is supported after this. # @example # allow(car).to receive(:go).and_throw(:out_of_gas) # allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1) def and_throw(*args) raise_already_invoked_error_if_necessary(__method__) self.terminal_implementation_action = Proc.new { throw(*args) } nil end # Tells the object to yield one or more args to a block when the message # is received. # # @return [MessageExpectation] self, to support further chaining. # @example # stream.stub(:open).and_yield(StringIO.new) def and_yield(*args, &block) raise_already_invoked_error_if_necessary(__method__) yield @eval_context = Object.new if block # Initialize args to yield now that it's being used, see also: comment # in constructor. @args_to_yield ||= [] @args_to_yield << args self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator) self end # @!endgroup # @!group Constraining Receive Counts # Constrain a message expectation to be received a specific number of # times. # # @return [MessageExpectation] self, to support further chaining. # @example # expect(dealer).to receive(:deal_card).exactly(10).times def exactly(n, &block) raise_already_invoked_error_if_necessary(__method__) self.inner_implementation_action = block set_expected_received_count :exactly, n self end # Constrain a message expectation to be received at least a specific # number of times. # # @return [MessageExpectation] self, to support further chaining. # @example # expect(dealer).to receive(:deal_card).at_least(9).times def at_least(n, &block) raise_already_invoked_error_if_necessary(__method__) set_expected_received_count :at_least, n if n == 0 raise "at_least(0) has been removed, use allow(...).to receive(:message) instead" end self.inner_implementation_action = block self end # Constrain a message expectation to be received at most a specific # number of times. # # @return [MessageExpectation] self, to support further chaining. # @example # expect(dealer).to receive(:deal_card).at_most(10).times def at_most(n, &block) raise_already_invoked_error_if_necessary(__method__) self.inner_implementation_action = block set_expected_received_count :at_most, n self end # Syntactic sugar for `exactly`, `at_least` and `at_most` # # @return [MessageExpectation] self, to support further chaining. # @example # expect(dealer).to receive(:deal_card).exactly(10).times # expect(dealer).to receive(:deal_card).at_least(10).times # expect(dealer).to receive(:deal_card).at_most(10).times def times(&block) self.inner_implementation_action = block self end alias time times # Expect a message not to be received at all. # # @return [MessageExpectation] self, to support further chaining. # @example # expect(car).to receive(:stop).never def never error_generator.raise_double_negation_error("expect(obj)") if negative? @expected_received_count = 0 self end # Expect a message to be received exactly one time. # # @return [MessageExpectation] self, to support further chaining. # @example # expect(car).to receive(:go).once def once(&block) self.inner_implementation_action = block set_expected_received_count :exactly, 1 self end # Expect a message to be received exactly two times. # # @return [MessageExpectation] self, to support further chaining. # @example # expect(car).to receive(:go).twice def twice(&block) self.inner_implementation_action = block set_expected_received_count :exactly, 2 self end # Expect a message to be received exactly three times. # # @return [MessageExpectation] self, to support further chaining. # @example # expect(car).to receive(:go).thrice def thrice(&block) self.inner_implementation_action = block set_expected_received_count :exactly, 3 self end # @!endgroup # @!group Other Constraints # Constrains a stub or message expectation to invocations with specific # arguments. # # With a stub, if the message might be received with other args as well, # you should stub a default value first, and then stub or mock the same # message using `with` to constrain to specific arguments. # # A message expectation will fail if the message is received with different # arguments. # # @return [MessageExpectation] self, to support further chaining. # @example # allow(cart).to receive(:add) { :failure } # allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success } # cart.add(Book.new(:isbn => 1234567890)) # # => :failure # cart.add(Book.new(:isbn => 1934356379)) # # => :success # # expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success } # cart.add(Book.new(:isbn => 1234567890)) # # => failed expectation # cart.add(Book.new(:isbn => 1934356379)) # # => passes def with(*args, &block) raise_already_invoked_error_if_necessary(__method__) if args.empty? raise ArgumentError, "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments." end self.inner_implementation_action = block @argument_list_matcher = ArgumentListMatcher.new(*args) self end ruby2_keywords(:with) if respond_to?(:ruby2_keywords, true) # Expect messages to be received in a specific order. # # @return [MessageExpectation] self, to support further chaining. # @example # expect(api).to receive(:prepare).ordered # expect(api).to receive(:run).ordered # expect(api).to receive(:finish).ordered def ordered(&block) if type == :stub RSpec.warning( "`allow(...).to receive(..).ordered` is not supported and will " \ "have no effect, use `and_return(*ordered_values)` instead." ) end self.inner_implementation_action = block additional_expected_calls.times do @order_group.register(self) end @ordered = true self end # @return [String] a nice representation of the message expectation def to_s args_description = error_generator.method_call_args_description(@argument_list_matcher.expected_args, "", "") { true } args_description = "(#{args_description})" unless args_description.start_with?("(") "#<#{self.class} #{error_generator.intro}.#{message}#{args_description}>" end alias inspect to_s # @private # Contains the parts of `MessageExpectation` that aren't part of # rspec-mocks' public API. The class is very big and could really use # some collaborators it delegates to for this stuff but for now this was # the simplest way to split the public from private stuff to make it # easier to publish the docs for the APIs we want published. module ImplementationDetails attr_accessor :error_generator, :implementation attr_reader :message attr_reader :orig_object attr_writer :expected_received_count, :expected_from, :argument_list_matcher protected :expected_received_count=, :expected_from=, :error_generator=, :implementation= # @private attr_reader :type # rubocop:disable Metrics/ParameterLists def initialize(error_generator, expectation_ordering, expected_from, method_double, type=:expectation, opts={}, &implementation_block) @type = type @error_generator = error_generator @error_generator.opts = error_generator.opts.merge(opts) @expected_from = expected_from @method_double = method_double @orig_object = @method_double.object @message = @method_double.method_name @actual_received_count = 0 @actual_received_count_write_mutex = Support::Mutex.new @expected_received_count = type == :expectation ? 1 : :any @argument_list_matcher = ArgumentListMatcher::MATCH_ALL @order_group = expectation_ordering @order_group.register(self) unless type == :stub @expectation_type = type @ordered = false @at_least = @at_most = @exactly = nil # Initialized to nil so that we don't allocate an array for every # mock or stub. See also comment in `and_yield`. @args_to_yield = nil @eval_context = nil @yield_receiver_to_implementation_block = false @implementation = Implementation.new self.inner_implementation_action = implementation_block end # rubocop:enable Metrics/ParameterLists def expected_args @argument_list_matcher.expected_args end def and_yield_receiver_to_implementation @yield_receiver_to_implementation_block = true self end def yield_receiver_to_implementation_block? @yield_receiver_to_implementation_block end def matches?(message, *args) @message == message && @argument_list_matcher.args_match?(*args) end ruby2_keywords :matches? if respond_to?(:ruby2_keywords, true) def safe_invoke(parent_stub, *args, &block) invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block) end ruby2_keywords :safe_invoke if respond_to?(:ruby2_keywords, true) def invoke(parent_stub, *args, &block) invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block) end ruby2_keywords :invoke if respond_to?(:ruby2_keywords, true) def invoke_without_incrementing_received_count(parent_stub, *args, &block) invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block) end ruby2_keywords :invoke_without_incrementing_received_count if respond_to?(:ruby2_keywords, true) def negative? @expected_received_count == 0 && !@at_least end def called_max_times? @expected_received_count != :any && !@at_least && @expected_received_count > 0 && @actual_received_count >= @expected_received_count end def matches_name_but_not_args(message, *args) @message == message && !@argument_list_matcher.args_match?(*args) end def verify_messages_received return if expected_messages_received? generate_error end def expected_messages_received? ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count? end def ensure_expected_ordering_received! @order_group.verify_invocation_order(self) if @ordered true end def ignoring_args? @expected_received_count == :any end def matches_at_least_count? @at_least && @actual_received_count >= @expected_received_count end def matches_at_most_count? @at_most && @actual_received_count <= @expected_received_count end def matches_exact_count? @expected_received_count == @actual_received_count end def similar_messages @similar_messages ||= [] end def advise(*args) similar_messages << args end def unadvise(args) similar_messages.delete_if { |message| args.include?(message) } end def generate_error if similar_messages.empty? @error_generator.raise_expectation_error( @message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, expected_args, @expected_from, exception_source_id ) else @error_generator.raise_similar_message_args_error( self, @similar_messages, @expected_from ) end end def raise_unexpected_message_args_error(args_for_multiple_calls) @error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls, exception_source_id) end def expectation_count_type return :at_least if @at_least return :at_most if @at_most nil end def description_for(verb) @error_generator.describe_expectation( verb, @message, @expected_received_count, @actual_received_count, expected_args ) end def raise_out_of_order_error @error_generator.raise_out_of_order_error @message end def additional_expected_calls return 0 if @expectation_type == :stub || !@exactly @expected_received_count - 1 end def ordered? @ordered end def negative_expectation_for?(message) @message == message && negative? end def actual_received_count_matters? @at_least || @at_most || @exactly end def increase_actual_received_count! @actual_received_count_write_mutex.synchronize do @actual_received_count += 1 end end private def exception_source_id @exception_source_id ||= "#{self.class.name} #{__id__}" end def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block) args.unshift(orig_object) if yield_receiver_to_implementation_block? if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count)) # args are the args we actually received, @argument_list_matcher is the # list of args we were expecting @error_generator.raise_expectation_error( @message, @expected_received_count, @argument_list_matcher, @actual_received_count + increment, expectation_count_type, args, nil, exception_source_id ) end @order_group.handle_order_constraint self if implementation.present? implementation.call(*args, &block) elsif parent_stub parent_stub.invoke(nil, *args, &block) end ensure @actual_received_count_write_mutex.synchronize do @actual_received_count += increment end end ruby2_keywords :invoke_incrementing_actual_calls_by if respond_to?(:ruby2_keywords, true) def has_been_invoked? @actual_received_count > 0 end def raise_already_invoked_error_if_necessary(calling_customization) return unless has_been_invoked? error_generator.raise_already_invoked_error(message, calling_customization) end def set_expected_received_count(relativity, n) raise "`count` is not supported with negative message expectations" if negative? @at_least = (relativity == :at_least) @at_most = (relativity == :at_most) @exactly = (relativity == :exactly) @expected_received_count = case n when Numeric then n when :once then 1 when :twice then 2 when :thrice then 3 end end def initial_implementation_action=(action) implementation.initial_action = action end def inner_implementation_action=(action) return unless action warn_about_stub_override if implementation.inner_action implementation.inner_action = action end def terminal_implementation_action=(action) implementation.terminal_action = action end def warn_about_stub_override RSpec.warning( "You're overriding a previous stub implementation of `#{@message}`. " \ "Called from #{CallerFilter.first_non_rspec_line}." ) end def wrap_original(method_name, &block) if RSpec::Mocks::TestDouble === @method_double.object @error_generator.raise_only_valid_on_a_partial_double(method_name) else warn_about_stub_override if implementation.inner_action @implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block) @yield_receiver_to_implementation_block = false end nil end end include ImplementationDetails end # Handles the implementation of an `and_yield` declaration. # @private class AndYieldImplementation def initialize(args_to_yield, eval_context, error_generator) @args_to_yield = args_to_yield @eval_context = eval_context @error_generator = error_generator end def call(*_args_to_ignore, &block) return if @args_to_yield.empty? && @eval_context.nil? @error_generator.raise_missing_block_error @args_to_yield unless block value = nil block_signature = Support::BlockSignature.new(block) @args_to_yield.each do |args| unless Support::StrictSignatureVerifier.new(block_signature, args).valid? @error_generator.raise_wrong_arity_error(args, block_signature) end value = @eval_context ? @eval_context.instance_exec(*args, &block) : yield(*args) end value end end # Handles the implementation of an `and_return` implementation. # @private class AndReturnImplementation def initialize(values_to_return) @values_to_return = values_to_return end def call(*_args_to_ignore, &_block) if @values_to_return.size > 1 @values_to_return.shift else @values_to_return.first end end end # Handles the implementation of an `and_invoke` implementation. # @private class AndInvokeImplementation def initialize(procs_to_invoke) @procs_to_invoke = procs_to_invoke end def call(*args, &block) proc = if @procs_to_invoke.size > 1 @procs_to_invoke.shift else @procs_to_invoke.first end proc.call(*args, &block) end end # Represents a configured implementation. Takes into account # any number of sub-implementations. # @private class Implementation attr_accessor :initial_action, :inner_action, :terminal_action def call(*args, &block) actions.map do |action| action.call(*args, &block) end.last end ruby2_keywords :call if respond_to?(:ruby2_keywords, true) def present? actions.any? end private def actions [initial_action, inner_action, terminal_action].compact end end # Represents an `and_call_original` implementation. # @private class AndWrapOriginalImplementation def initialize(method, block) @method = method @block = block end CannotModifyFurtherError = Class.new(StandardError) def initial_action=(_value) raise cannot_modify_further_error end def inner_action=(_value) raise cannot_modify_further_error end def terminal_action=(_value) raise cannot_modify_further_error end def present? true end def inner_action true end def call(*args, &block) @block.call(@method, *args, &block) end ruby2_keywords :call if respond_to?(:ruby2_keywords, true) private def cannot_modify_further_error CannotModifyFurtherError.new "This method has already been configured " \ "to call the original implementation, and cannot be modified further." end end end end rspec-mocks-3.13.0/lib/rspec/mocks/method_double.rb000066400000000000000000000257431455767030500222100ustar00rootroot00000000000000module RSpec module Mocks # @private class MethodDouble # @private TODO: drop in favor of FrozenError in ruby 2.5+ FROZEN_ERROR_MSG = /can't modify frozen/ # @private attr_reader :method_name, :object, :expectations, :stubs, :method_stasher # @private def initialize(object, method_name, proxy) @method_name = method_name @object = object @proxy = proxy @original_visibility = nil @method_stasher = InstanceMethodStasher.new(object, method_name) @method_is_proxied = false @expectations = [] @stubs = [] end def original_implementation_callable # If original method is not present, uses the `method_missing` # handler of the object. This accounts for cases where the user has not # correctly defined `respond_to?`, and also 1.8 which does not provide # method handles for missing methods even if `respond_to?` is correct. @original_implementation_callable ||= original_method || method_missing_block end alias_method :save_original_implementation_callable!, :original_implementation_callable def original_method @original_method ||= @method_stasher.original_method || @proxy.original_method_handle_for(method_name) end # @private def method_missing_block block = Proc.new do |*args, &b| @object.__send__(:method_missing, @method_name, *args, &b) end block.ruby2_keywords if block.respond_to?(:ruby2_keywords) block end # @private def visibility @proxy.visibility_for(@method_name) end # @private def object_singleton_class class << @object; self; end end # @private def configure_method @original_visibility = visibility @method_stasher.stash unless @method_is_proxied define_proxy_method end # @private def define_proxy_method return if @method_is_proxied save_original_implementation_callable! definition_target.class_exec(self, method_name, @original_visibility || visibility) do |method_double, method_name, visibility| define_method(method_name) do |*args, &block| method_double.proxy_method_invoked(self, *args, &block) end # This can't be `if respond_to?(:ruby2_keywords, true)`, # see https://github.com/rspec/rspec-mocks/pull/1385#issuecomment-755340298 ruby2_keywords(method_name) if Module.private_method_defined?(:ruby2_keywords) __send__(visibility, method_name) end @method_is_proxied = true rescue RuntimeError, TypeError => e # TODO: drop in favor of FrozenError in ruby 2.5+ # RuntimeError (and FrozenError) for ruby 2.x # TypeError for ruby 1.x if (defined?(FrozenError) && e.is_a?(FrozenError)) || FROZEN_ERROR_MSG === e.message raise ArgumentError, "Cannot proxy frozen objects, rspec-mocks relies on proxies for method stubbing and expectations." end raise end # The implementation of the proxied method. Subclasses may override this # method to perform additional operations. # # @private def proxy_method_invoked(_obj, *args, &block) @proxy.message_received method_name, *args, &block end ruby2_keywords :proxy_method_invoked if respond_to?(:ruby2_keywords, true) # @private def restore_original_method return unless @method_is_proxied remove_method_from_definition_target @method_stasher.restore if @method_stasher.method_is_stashed? restore_original_visibility @method_is_proxied = false rescue RuntimeError, TypeError => e # TODO: drop in favor of FrozenError in ruby 2.5+ # RuntimeError (and FrozenError) for ruby 2.x # TypeError for ruby 1.x if (defined?(FrozenError) && e.is_a?(FrozenError)) || FROZEN_ERROR_MSG === e.message return show_frozen_warning end raise end # @private def show_frozen_warning RSpec.warn_with( "WARNING: rspec-mocks was unable to restore the original `#{@method_name}` " \ "method on #{@object.inspect} because it has been frozen. If you reuse this " \ "object, `#{@method_name}` will continue to respond with its stub implementation.", :call_site => nil, :use_spec_location_as_call_site => true ) end # @private def restore_original_visibility return unless @original_visibility && MethodReference.method_defined_at_any_visibility?(object_singleton_class, @method_name) object_singleton_class.__send__(@original_visibility, method_name) end # @private def verify expectations.each { |e| e.verify_messages_received } end # @private def reset restore_original_method clear end # @private def clear expectations.clear stubs.clear end # The type of message expectation to create has been extracted to its own # method so that subclasses can override it. # # @private def message_expectation_class MessageExpectation end # @private def add_expectation(error_generator, expectation_ordering, expected_from, opts, &implementation) configure_method expectation = message_expectation_class.new(error_generator, expectation_ordering, expected_from, self, :expectation, opts, &implementation) expectations << expectation expectation end # @private def build_expectation(error_generator, expectation_ordering) expected_from = IGNORED_BACKTRACE_LINE message_expectation_class.new(error_generator, expectation_ordering, expected_from, self) end # @private def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation) configure_method stub = message_expectation_class.new(error_generator, expectation_ordering, expected_from, self, :stub, opts, &implementation) stubs.unshift stub stub end # A simple stub can only return a concrete value for a message, and # cannot match on arguments. It is used as an optimization over # `add_stub` / `add_expectation` where it is known in advance that this # is all that will be required of a stub, such as when passing attributes # to the `double` example method. They do not stash or restore existing method # definitions. # # @private def add_simple_stub(method_name, response) setup_simple_method_double method_name, response, stubs end # @private def add_simple_expectation(method_name, response, error_generator, backtrace_line) setup_simple_method_double method_name, response, expectations, error_generator, backtrace_line end # @private def setup_simple_method_double(method_name, response, collection, error_generator=nil, backtrace_line=nil) define_proxy_method me = SimpleMessageExpectation.new(method_name, response, error_generator, backtrace_line) collection.unshift me me end # @private def add_default_stub(*args, &implementation) return if stubs.any? add_stub(*args, &implementation) end # @private def remove_stub raise_method_not_stubbed_error if stubs.empty? remove_stub_if_present end # @private def remove_stub_if_present expectations.empty? ? reset : stubs.clear end # @private def raise_method_not_stubbed_error RSpec::Mocks.error_generator.raise_method_not_stubbed_error(method_name) end # In Ruby 2.0.0 and above prepend will alter the method lookup chain. # We use an object's singleton class to define method doubles upon, # however if the object has had its singleton class (as opposed to # its actual class) prepended too then the the method lookup chain # will look in the prepended module first, **before** the singleton # class. # # This code works around that by providing a mock definition target # that is either the singleton class, or if necessary, a prepended module # of our own. # if Support::RubyFeatures.module_prepends_supported? private # We subclass `Module` in order to be able to easily detect our prepended module. RSpecPrependedModule = Class.new(Module) def definition_target @definition_target ||= usable_rspec_prepended_module || object_singleton_class end def usable_rspec_prepended_module @proxy.prepended_modules_of_singleton_class.each do |mod| # If we have one of our modules prepended before one of the user's # modules that defines the method, use that, since our module's # definition will take precedence. return mod if RSpecPrependedModule === mod # If we hit a user module with the method defined first, # we must create a new prepend module, even if one exists later, # because ours will only take precedence if it comes first. return new_rspec_prepended_module if mod.method_defined?(method_name) end nil end def new_rspec_prepended_module RSpecPrependedModule.new.tap do |mod| object_singleton_class.__send__ :prepend, mod end end else private def definition_target object_singleton_class end end private def remove_method_from_definition_target definition_target.__send__(:remove_method, @method_name) rescue NameError # This can happen when the method has been monkeyed with by # something outside RSpec. This happens, for example, when # `file.write` has been stubbed, and then `file.reopen(other_io)` # is later called, as `File#reopen` appears to redefine `write`. # # Note: we could avoid rescuing this by checking # `definition_target.instance_method(@method_name).owner == definition_target`, # saving us from the cost of the expensive exception, but this error is # extremely rare (it was discovered on 2014-12-30, only happens on # RUBY_VERSION < 2.0 and our spec suite only hits this condition once), # so we'd rather avoid the cost of that check for every method double, # and risk the rare situation where this exception will get raised. RSpec.warn_with( "WARNING: RSpec could not fully restore #{@object.inspect}." \ "#{@method_name}, possibly because the method has been redefined " \ "by something outside of RSpec." ) end end end end rspec-mocks-3.13.0/lib/rspec/mocks/method_reference.rb000066400000000000000000000161151455767030500226650ustar00rootroot00000000000000RSpec::Support.require_rspec_support 'comparable_version' module RSpec module Mocks # Represents a method on an object that may or may not be defined. # The method may be an instance method on a module or a method on # any object. # # @private class MethodReference def self.for(object_reference, method_name) new(object_reference, method_name) end def initialize(object_reference, method_name) @object_reference = object_reference @method_name = method_name end # A method is implemented if sending the message does not result in # a `NoMethodError`. It might be dynamically implemented by # `method_missing`. def implemented? @object_reference.when_loaded do |m| method_implemented?(m) end end # Returns true if we definitively know that sending the method # will result in a `NoMethodError`. # # This is not simply the inverse of `implemented?`: there are # cases when we don't know if a method is implemented and # both `implemented?` and `unimplemented?` will return false. def unimplemented? @object_reference.when_loaded do |_m| return !implemented? end # If it's not loaded, then it may be implemented but we can't check. false end # A method is defined if we are able to get a `Method` object for it. # In that case, we can assert against metadata like the arity. def defined? @object_reference.when_loaded do |m| method_defined?(m) end end def with_signature return unless (original = original_method) yield Support::MethodSignature.new(original) end def visibility @object_reference.when_loaded do |m| return visibility_from(m) end # When it's not loaded, assume it's public. We don't want to # wrongly treat the method as private. :public end def self.instance_method_visibility_for(klass, method_name) if klass.public_method_defined?(method_name) :public elsif klass.private_method_defined?(method_name) :private elsif klass.protected_method_defined?(method_name) :protected end end class << self alias method_defined_at_any_visibility? instance_method_visibility_for end def self.method_visibility_for(object, method_name) vis = instance_method_visibility_for(class << object; self; end, method_name) # If the method is not defined on the class, `instance_method_visibility_for` # returns `nil`. However, it may be handled dynamically by `method_missing`, # so here we check `respond_to` (passing false to not check private methods). # # This only considers the public case, but I don't think it's possible to # write `method_missing` in such a way that it handles a dynamic message # with private or protected visibility. Ruby doesn't provide you with # the caller info. return vis unless vis.nil? proxy = RSpec::Mocks.space.proxy_for(object) respond_to = proxy.method_double_if_exists_for_message(:respond_to?) visible = respond_to && respond_to.original_method.call(method_name) || object.respond_to?(method_name) return :public if visible end private def original_method @object_reference.when_loaded do |m| self.defined? && find_method(m) end end end # @private class InstanceMethodReference < MethodReference private def method_implemented?(mod) MethodReference.method_defined_at_any_visibility?(mod, @method_name) end # Ideally, we'd use `respond_to?` for `method_implemented?` but we need a # reference to an instance to do that and we don't have one. Note that # we may get false negatives: if the method is implemented via # `method_missing`, we'll return `false` even though it meets our # definition of "implemented". However, it's the best we can do. alias method_defined? method_implemented? # works around the fact that repeated calls for method parameters will # falsely return empty arrays on JRuby in certain circumstances, this # is necessary here because we can't dup/clone UnboundMethods. # # This is necessary due to a bug in JRuby prior to 1.7.5 fixed in: # https://github.com/jruby/jruby/commit/99a0613fe29935150d76a9a1ee4cf2b4f63f4a27 if RUBY_PLATFORM == 'java' && RSpec::Support::ComparableVersion.new(JRUBY_VERSION) < '1.7.5' def find_method(mod) mod.dup.instance_method(@method_name) end else def find_method(mod) mod.instance_method(@method_name) end end def visibility_from(mod) MethodReference.instance_method_visibility_for(mod, @method_name) end end # @private class ObjectMethodReference < MethodReference def self.for(object_reference, method_name) if ClassNewMethodReference.applies_to?(method_name) { object_reference.when_loaded { |o| o } } ClassNewMethodReference.new(object_reference, method_name) else super end end private def method_implemented?(object) object.respond_to?(@method_name, true) end def method_defined?(object) (class << object; self; end).method_defined?(@method_name) end def find_method(object) object.method(@method_name) end def visibility_from(object) MethodReference.method_visibility_for(object, @method_name) end end # When a class's `.new` method is stubbed, we want to use the method # signature from `#initialize` because `.new`'s signature is a generic # `def new(*args)` and it simply delegates to `#initialize` and forwards # all args...so the method with the actually used signature is `#initialize`. # # This method reference implementation handles that specific case. # @private class ClassNewMethodReference < ObjectMethodReference def self.applies_to?(method_name) return false unless method_name == :new klass = yield return false unless ::Class === klass && klass.respond_to?(:new, true) # We only want to apply our special logic to normal `new` methods. # Methods that the user has monkeyed with should be left as-is. uses_class_new?(klass) end if RUBY_VERSION.to_i >= 3 CLASS_NEW = ::Class.instance_method(:new) def self.uses_class_new?(klass) ::RSpec::Support.method_handle_for(klass, :new) == CLASS_NEW.bind(klass) end else # Ruby 2's Method#== is too strict def self.uses_class_new?(klass) ::RSpec::Support.method_handle_for(klass, :new).owner == ::Class end end def with_signature @object_reference.when_loaded do |klass| yield Support::MethodSignature.new(klass.instance_method(:initialize)) end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/minitest_integration.rb000066400000000000000000000034751455767030500236330ustar00rootroot00000000000000require 'rspec/mocks' module RSpec module Mocks # @private module MinitestIntegration include ::RSpec::Mocks::ExampleMethods def before_setup ::RSpec::Mocks.setup super end def after_teardown super # Only verify if there's not already an error. Otherwise # we risk getting the same failure twice, since negative # expectation violations raise both when the message is # unexpectedly received, and also during `verify` (in case # the first failure was caught by user code via a # `rescue Exception`). ::RSpec::Mocks.verify unless failures.any? ensure ::RSpec::Mocks.teardown end end end end Minitest::Test.send(:include, RSpec::Mocks::MinitestIntegration) if defined?(::Minitest::Expectation) if defined?(::RSpec::Expectations) && ::Minitest::Expectation.method_defined?(:to) # rspec/expectations/minitest_integration has already been loaded and # has defined `to`/`not_to`/`to_not` on `Minitest::Expectation` so we do # not want to here (or else we would interfere with rspec-expectations' definition). else # ...otherwise, define those methods now. If `rspec/expectations/minitest_integration` # is loaded after this file, it'll override the definition here. Minitest::Expectation.class_eval do include RSpec::Mocks::ExpectationTargetMethods def to(*args) ctx.assertions += 1 super end def not_to(*args) ctx.assertions += 1 super end def to_not(*args) ctx.assertions += 1 super end end end end module RSpec module Mocks remove_const :MockExpectationError # Raised when a message expectation is not satisfied. MockExpectationError = ::Minitest::Assertion end end rspec-mocks-3.13.0/lib/rspec/mocks/mutate_const.rb000066400000000000000000000250241455767030500220730ustar00rootroot00000000000000RSpec::Support.require_rspec_support 'recursive_const_methods' module RSpec module Mocks # Provides information about constants that may (or may not) # have been mutated by rspec-mocks. class Constant extend Support::RecursiveConstMethods # @api private def initialize(name) @name = name @previously_defined = false @stubbed = false @hidden = false @valid_name = true yield self if block_given? end # @return [String] The fully qualified name of the constant. attr_reader :name # @return [Object, nil] The original value (e.g. before it # was mutated by rspec-mocks) of the constant, or # nil if the constant was not previously defined. attr_accessor :original_value # @private attr_writer :previously_defined, :stubbed, :hidden, :valid_name # @return [Boolean] Whether or not the constant was defined # before the current example. def previously_defined? @previously_defined end # @return [Boolean] Whether or not rspec-mocks has mutated # (stubbed or hidden) this constant. def mutated? @stubbed || @hidden end # @return [Boolean] Whether or not rspec-mocks has stubbed # this constant. def stubbed? @stubbed end # @return [Boolean] Whether or not rspec-mocks has hidden # this constant. def hidden? @hidden end # @return [Boolean] Whether or not the provided constant name # is a valid Ruby constant name. def valid_name? @valid_name end # The default `to_s` isn't very useful, so a custom version is provided. def to_s "#<#{self.class.name} #{name}>" end alias inspect to_s # @private def self.unmutated(name) previously_defined = !!recursive_const_defined?(name) rescue NameError new(name) do |c| c.valid_name = false end else new(name) do |const| const.previously_defined = previously_defined const.original_value = recursive_const_get(name) if previously_defined end end # Queries rspec-mocks to find out information about the named constant. # # @param [String] name the name of the constant # @return [Constant] an object containing information about the named # constant. def self.original(name) mutator = ::RSpec::Mocks.space.constant_mutator_for(name) mutator ? mutator.to_constant : unmutated(name) end end # Provides a means to stub constants. class ConstantMutator extend Support::RecursiveConstMethods # Stubs a constant. # # @param (see ExampleMethods#stub_const) # @option (see ExampleMethods#stub_const) # @return (see ExampleMethods#stub_const) # # @see ExampleMethods#stub_const # @note It's recommended that you use `stub_const` in your # examples. This is an alternate public API that is provided # so you can stub constants in other contexts (e.g. helper # classes). def self.stub(constant_name, value, options={}) unless String === constant_name raise ArgumentError, "`stub_const` requires a String, but you provided a #{constant_name.class.name}" end mutator = if recursive_const_defined?(constant_name, &raise_on_invalid_const) DefinedConstantReplacer else UndefinedConstantSetter end mutate(mutator.new(constant_name, value, options[:transfer_nested_constants])) value end # Hides a constant. # # @param (see ExampleMethods#hide_const) # # @see ExampleMethods#hide_const # @note It's recommended that you use `hide_const` in your # examples. This is an alternate public API that is provided # so you can hide constants in other contexts (e.g. helper # classes). def self.hide(constant_name) mutate(ConstantHider.new(constant_name, nil, {})) nil end # Contains common functionality used by all of the constant mutators. # # @private class BaseMutator include Support::RecursiveConstMethods attr_reader :original_value, :full_constant_name def initialize(full_constant_name, mutated_value, transfer_nested_constants) @full_constant_name = normalize_const_name(full_constant_name) @mutated_value = mutated_value @transfer_nested_constants = transfer_nested_constants @context_parts = @full_constant_name.split('::') @const_name = @context_parts.pop @reset_performed = false end def to_constant const = Constant.new(full_constant_name) const.original_value = original_value const end def idempotently_reset reset unless @reset_performed @reset_performed = true end end # Hides a defined constant for the duration of an example. # # @private class ConstantHider < BaseMutator def mutate return unless (@defined = recursive_const_defined?(full_constant_name)) @context = recursive_const_get(@context_parts.join('::')) @original_value = get_const_defined_on(@context, @const_name) @context.__send__(:remove_const, @const_name) end def to_constant return Constant.unmutated(full_constant_name) unless @defined const = super const.hidden = true const.previously_defined = true const end def reset return unless @defined @context.const_set(@const_name, @original_value) end end # Replaces a defined constant for the duration of an example. # # @private class DefinedConstantReplacer < BaseMutator def initialize(*args) super @constants_to_transfer = [] end def mutate @context = recursive_const_get(@context_parts.join('::')) @original_value = get_const_defined_on(@context, @const_name) @constants_to_transfer = verify_constants_to_transfer! @context.__send__(:remove_const, @const_name) @context.const_set(@const_name, @mutated_value) transfer_nested_constants end def to_constant const = super const.stubbed = true const.previously_defined = true const end def reset @constants_to_transfer.each do |const| @mutated_value.__send__(:remove_const, const) end @context.__send__(:remove_const, @const_name) @context.const_set(@const_name, @original_value) end def transfer_nested_constants @constants_to_transfer.each do |const| @mutated_value.const_set(const, get_const_defined_on(original_value, const)) end end def verify_constants_to_transfer! return [] unless should_transfer_nested_constants? { @original_value => "the original value", @mutated_value => "the stubbed value" }.each do |value, description| next if value.respond_to?(:constants) raise ArgumentError, "Cannot transfer nested constants for #{@full_constant_name} " \ "since #{description} is not a class or module and only classes " \ "and modules support nested constants." end if Array === @transfer_nested_constants @transfer_nested_constants = @transfer_nested_constants.map(&:to_s) if RUBY_VERSION == '1.8.7' undefined_constants = @transfer_nested_constants - constants_defined_on(@original_value) if undefined_constants.any? available_constants = constants_defined_on(@original_value) - @transfer_nested_constants raise ArgumentError, "Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " \ "for #{@full_constant_name} since they are not defined. Did you mean " \ "#{available_constants.join(' or ')}?" end @transfer_nested_constants else constants_defined_on(@original_value) end end def should_transfer_nested_constants? return true if @transfer_nested_constants return false unless RSpec::Mocks.configuration.transfer_nested_constants? @original_value.respond_to?(:constants) && @mutated_value.respond_to?(:constants) end end # Sets an undefined constant for the duration of an example. # # @private class UndefinedConstantSetter < BaseMutator def mutate @parent = @context_parts.inject(Object) do |klass, name| if const_defined_on?(klass, name) get_const_defined_on(klass, name) else ConstantMutator.stub(name_for(klass, name), Module.new) end end @parent.const_set(@const_name, @mutated_value) end def to_constant const = super const.stubbed = true const.previously_defined = false const end def reset @parent.__send__(:remove_const, @const_name) end private def name_for(parent, name) root = if parent == Object '' else parent.name end root + '::' + name end end # Uses the mutator to mutate (stub or hide) a constant. Ensures that # the mutator is correctly registered so it can be backed out at the end # of the test. # # @private def self.mutate(mutator) ::RSpec::Mocks.space.register_constant_mutator(mutator) mutator.mutate end # Used internally by the constant stubbing to raise a helpful # error when a constant like "A::B::C" is stubbed and A::B is # not a module (and thus, it's impossible to define "A::B::C" # since only modules can have nested constants). # # @api private def self.raise_on_invalid_const lambda do |const_name, failed_name| raise "Cannot stub constant #{failed_name} on #{const_name} " \ "since #{const_name} is not a module." end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/object_reference.rb000066400000000000000000000110531455767030500226470ustar00rootroot00000000000000module RSpec module Mocks # @private class ObjectReference # Returns an appropriate Object or Module reference based # on the given argument. def self.for(object_module_or_name, allow_direct_object_refs=false) case object_module_or_name when Module if anonymous_module?(object_module_or_name) DirectObjectReference.new(object_module_or_name) else # Use a `NamedObjectReference` if it has a name because this # will use the original value of the constant in case it has # been stubbed. NamedObjectReference.new(name_of(object_module_or_name)) end when String NamedObjectReference.new(object_module_or_name) else if allow_direct_object_refs DirectObjectReference.new(object_module_or_name) else raise ArgumentError, "Module or String expected, got #{object_module_or_name.inspect}" end end end if Module.new.name.nil? def self.anonymous_module?(mod) !name_of(mod) end else # 1.8.7 def self.anonymous_module?(mod) name_of(mod) == "" end end private_class_method :anonymous_module? def self.name_of(mod) MODULE_NAME_METHOD.bind(mod).call end private_class_method :name_of # @private MODULE_NAME_METHOD = Module.instance_method(:name) end # An implementation of rspec-mocks' reference interface. # Used when an object is passed to {ExampleMethods#object_double}, or # an anonymous class or module is passed to {ExampleMethods#instance_double} # or {ExampleMethods#class_double}. # Represents a reference to that object. # @see NamedObjectReference class DirectObjectReference # @param object [Object] the object to which this refers def initialize(object) @object = object end # @return [String] the object's description (via `#inspect`). def description @object.inspect end # Defined for interface parity with the other object reference # implementations. Raises an `ArgumentError` to indicate that `as_stubbed_const` # is invalid when passing an object argument to `object_double`. def const_to_replace raise ArgumentError, "Can not perform constant replacement with an anonymous object." end # The target of the verifying double (the object itself). # # @return [Object] def target @object end # Always returns true for an object as the class is defined. # # @return [true] def defined? true end # Yields if the reference target is loaded, providing a generic mechanism # to optionally run a bit of code only when a reference's target is # loaded. # # This specific implementation always yields because direct references # are always loaded. # # @yield [Object] the target of this reference. def when_loaded yield @object end end # An implementation of rspec-mocks' reference interface. # Used when a string is passed to {ExampleMethods#object_double}, # and when a string, named class or named module is passed to # {ExampleMethods#instance_double}, or {ExampleMethods#class_double}. # Represents a reference to the object named (via a constant lookup) # by the string. # @see DirectObjectReference class NamedObjectReference # @param const_name [String] constant name def initialize(const_name) @const_name = const_name end # @return [Boolean] true if the named constant is defined, false otherwise. def defined? !!object end # @return [String] the constant name to replace with a double. def const_to_replace @const_name end alias description const_to_replace # @return [Object, nil] the target of the verifying double (the named object), or # nil if it is not defined. def target object end # Yields if the reference target is loaded, providing a generic mechanism # to optionally run a bit of code only when a reference's target is # loaded. # # @yield [Object] the target object def when_loaded yield object if object end private def object return @object if defined?(@object) @object = Constant.original(@const_name).original_value end end end end rspec-mocks-3.13.0/lib/rspec/mocks/order_group.rb000066400000000000000000000034751455767030500217230ustar00rootroot00000000000000module RSpec module Mocks # @private class OrderGroup def initialize @expectations = [] @invocation_order = [] @index = 0 end # @private def register(expectation) @expectations << expectation end def invoked(message) @invocation_order << message end # @private def ready_for?(expectation) remaining_expectations.find(&:ordered?) == expectation end # @private def consume remaining_expectations.each_with_index do |expectation, index| next unless expectation.ordered? @index += index + 1 return expectation end nil end # @private def handle_order_constraint(expectation) return unless expectation.ordered? && remaining_expectations.include?(expectation) return consume if ready_for?(expectation) expectation.raise_out_of_order_error end def verify_invocation_order(expectation) expectation.raise_out_of_order_error unless expectations_invoked_in_order? true end def clear @index = 0 @invocation_order.clear @expectations.clear end def empty? @expectations.empty? end private def remaining_expectations @expectations[@index..-1] || [] end def expectations_invoked_in_order? invoked_expectations == expected_invocations end def invoked_expectations @expectations.select { |e| e.ordered? && @invocation_order.include?(e) } end def expected_invocations @invocation_order.map { |invocation| expectation_for(invocation) }.compact end def expectation_for(message) @expectations.find { |e| message == e } end end end end rspec-mocks-3.13.0/lib/rspec/mocks/proxy.rb000066400000000000000000000417121455767030500205510ustar00rootroot00000000000000module RSpec module Mocks # @private class Proxy # @private SpecificMessage = Struct.new(:object, :message, :args) do def ==(expectation) expectation.orig_object == object && expectation.matches?(message, *args) end end unless defined?(Mutex) Support.require_rspec_support 'mutex' Mutex = Support::Mutex end # @private def ensure_implemented(*_args) # noop for basic proxies, see VerifyingProxy for behaviour. end # @private def initialize(object, order_group, options={}) ensure_can_be_proxied!(object) @object = object @order_group = order_group @error_generator = ErrorGenerator.new(object) @messages_received = [] @messages_received_mutex = Mutex.new @options = options @null_object = false @method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) } end # @private def ensure_can_be_proxied!(object) return unless object.is_a?(Symbol) msg = "Cannot proxy frozen objects. Symbols such as #{object} cannot be mocked or stubbed." raise ArgumentError, msg end # @private attr_reader :object # @private def null_object? @null_object end # @private # Tells the object to ignore any messages that aren't explicitly set as # stubs or message expectations. def as_null_object @null_object = true @object end # @private def original_method_handle_for(_message) nil end DEFAULT_MESSAGE_EXPECTATION_OPTS = {}.freeze # @private def add_message_expectation(method_name, opts=DEFAULT_MESSAGE_EXPECTATION_OPTS, &block) location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } meth_double = method_double_for(method_name) if null_object? && !block meth_double.add_default_stub(@error_generator, @order_group, location, opts) do @object end end meth_double.add_expectation @error_generator, @order_group, location, opts, &block end # @private def add_simple_expectation(method_name, response, location) method_double_for(method_name).add_simple_expectation method_name, response, @error_generator, location end # @private def build_expectation(method_name) meth_double = method_double_for(method_name) meth_double.build_expectation( @error_generator, @order_group ) end # @private def replay_received_message_on(expectation, &block) expected_method_name = expectation.message meth_double = method_double_for(expected_method_name) if meth_double.expectations.any? @error_generator.raise_expectation_on_mocked_method(expected_method_name) end unless null_object? || meth_double.stubs.any? @error_generator.raise_expectation_on_unstubbed_method(expected_method_name) end @messages_received_mutex.synchronize do @messages_received.each do |(actual_method_name, args, received_block)| next unless expectation.matches?(actual_method_name, *args) expectation.safe_invoke(nil) block.call(*args, &received_block) if block end end end # @private def check_for_unexpected_arguments(expectation) @messages_received_mutex.synchronize do return if @messages_received.empty? return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) } name_but_not_args, others = @messages_received.partition do |(method_name, args, _)| expectation.matches_name_but_not_args(method_name, *args) end return if name_but_not_args.empty? && !others.empty? expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] }) end end # @private def add_stub(method_name, opts={}, &implementation) location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } method_double_for(method_name).add_stub @error_generator, @order_group, location, opts, &implementation end # @private def add_simple_stub(method_name, response) method_double_for(method_name).add_simple_stub method_name, response end # @private def remove_stub(method_name) method_double_for(method_name).remove_stub end # @private def remove_stub_if_present(method_name) method_double_for(method_name).remove_stub_if_present end # @private def verify @method_doubles.each_value { |d| d.verify } end # @private def reset @messages_received_mutex.synchronize do @messages_received.clear end end # @private def received_message?(method_name, *args, &block) @messages_received_mutex.synchronize do @messages_received.any? { |array| array == [method_name, args, block] } end end # @private def messages_arg_list @messages_received_mutex.synchronize do @messages_received.map { |_, args, _| args } end end # @private def has_negative_expectation?(message) method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) } end # @private def record_message_received(message, *args, &block) @order_group.invoked SpecificMessage.new(object, message, args) @messages_received_mutex.synchronize do @messages_received << [message, args, block] end end ruby2_keywords :record_message_received if respond_to?(:ruby2_keywords, true) # @private def message_received(message, *args, &block) record_message_received message, *args, &block expectation = find_matching_expectation(message, *args) stub = find_matching_method_stub(message, *args) if (stub && expectation && expectation.called_max_times?) || (stub && !expectation) expectation.increase_actual_received_count! if expectation && expectation.actual_received_count_matters? if (expectation = find_almost_matching_expectation(message, *args)) expectation.advise(*args) unless expectation.expected_messages_received? end stub.invoke(nil, *args, &block) elsif expectation expectation.unadvise(messages_arg_list) expectation.invoke(stub, *args, &block) elsif (expectation = find_almost_matching_expectation(message, *args)) expectation.advise(*args) if null_object? unless expectation.expected_messages_received? if null_object? || !has_negative_expectation?(message) expectation.raise_unexpected_message_args_error([args]) end elsif (stub = find_almost_matching_stub(message, *args)) stub.advise(*args) raise_missing_default_stub_error(stub, [args]) elsif Class === @object @object.superclass.__send__(message, *args, &block) else @object.__send__(:method_missing, message, *args, &block) end end ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true) # @private def raise_unexpected_message_error(method_name, args) @error_generator.raise_unexpected_message_error method_name, args end # @private def raise_missing_default_stub_error(expectation, args_for_multiple_calls) @error_generator.raise_missing_default_stub_error(expectation, args_for_multiple_calls) end # @private def visibility_for(_method_name) # This is the default (for test doubles). Subclasses override this. :public end if Support::RubyFeatures.module_prepends_supported? def self.prepended_modules_of(klass) ancestors = klass.ancestors # `|| 0` is necessary for Ruby 2.0, where the singleton class # is only in the ancestor list when there are prepended modules. singleton_index = ancestors.index(klass) || 0 ancestors[0, singleton_index] end def prepended_modules_of_singleton_class @prepended_modules_of_singleton_class ||= RSpec::Mocks::Proxy.prepended_modules_of(@object.singleton_class) end end # @private def method_double_if_exists_for_message(message) method_double_for(message) if @method_doubles.key?(message.to_sym) end private def method_double_for(message) @method_doubles[message.to_sym] end def find_matching_expectation(method_name, *args) find_best_matching_expectation_for(method_name) do |expectation| expectation.matches?(method_name, *args) end end ruby2_keywords :find_matching_expectation if respond_to?(:ruby2_keywords, true) def find_almost_matching_expectation(method_name, *args) find_best_matching_expectation_for(method_name) do |expectation| expectation.matches_name_but_not_args(method_name, *args) end end ruby2_keywords :find_almost_matching_expectation if respond_to?(:ruby2_keywords, true) def find_best_matching_expectation_for(method_name) first_match = nil method_double_for(method_name).expectations.each do |expectation| next unless yield expectation return expectation unless expectation.called_max_times? first_match ||= expectation end first_match end def find_matching_method_stub(method_name, *args) method_double_for(method_name).stubs.find { |stub| stub.matches?(method_name, *args) } end ruby2_keywords :find_matching_method_stub if respond_to?(:ruby2_keywords, true) def find_almost_matching_stub(method_name, *args) method_double_for(method_name).stubs.find { |stub| stub.matches_name_but_not_args(method_name, *args) } end ruby2_keywords :find_almost_matching_stub if respond_to?(:ruby2_keywords, true) end # @private class TestDoubleProxy < Proxy def reset @method_doubles.clear object.__disallow_further_usage! super end end # @private class PartialDoubleProxy < Proxy def original_method_handle_for(message) if any_instance_class_recorder_observing_method?(@object.class, message) message = ::RSpec::Mocks.space. any_instance_recorder_for(@object.class). build_alias_method_name(message) end ::RSpec::Support.method_handle_for(@object, message) rescue NameError nil end # @private def add_simple_expectation(method_name, response, location) method_double_for(method_name).configure_method super end # @private def add_simple_stub(method_name, response) method_double_for(method_name).configure_method super end # @private def visibility_for(method_name) # We fall back to :public because by default we allow undefined methods # to be stubbed, and when we do so, we make them public. MethodReference.method_visibility_for(@object, method_name) || :public end def reset @method_doubles.each_value { |d| d.reset } super end def message_received(message, *args, &block) RSpec::Mocks.space.any_instance_recorders_from_ancestry_of(object).each do |subscriber| subscriber.notify_received_message(object, message, args, block) end super end ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true) private def any_instance_class_recorder_observing_method?(klass, method_name) only_return_existing = true recorder = ::RSpec::Mocks.space.any_instance_recorder_for(klass, only_return_existing) return true if recorder && recorder.already_observing?(method_name) superklass = klass.superclass return false if superklass.nil? any_instance_class_recorder_observing_method?(superklass, method_name) end end # @private # When we mock or stub a method on a class, we have to treat it a bit different, # because normally singleton method definitions only affect the object on which # they are defined, but on classes they affect subclasses, too. As a result, # we need some special handling to get the original method. module PartialClassDoubleProxyMethods def initialize(source_space, *args) @source_space = source_space super(*args) end # Consider this situation: # # class A; end # class B < A; end # # allow(A).to receive(:new) # expect(B).to receive(:new).and_call_original # # When getting the original definition for `B.new`, we cannot rely purely on # using `B.method(:new)` before our redefinition is defined on `B`, because # `B.method(:new)` will return a method that will execute the stubbed version # of the method on `A` since singleton methods on classes are in the lookup # hierarchy. # # To do it properly, we need to find the original definition of `new` from `A` # from _before_ `A` was stubbed, and we need to rebind it to `B` so that it will # run with the proper `self`. # # That's what this method (together with `original_unbound_method_handle_from_ancestor_for`) # does. def original_method_handle_for(message) unbound_method = superclass_proxy && superclass_proxy.original_unbound_method_handle_from_ancestor_for(message.to_sym) return super unless unbound_method unbound_method.bind(object) # :nocov: rescue TypeError if RUBY_VERSION == '1.8.7' # In MRI 1.8.7, a singleton method on a class cannot be rebound to its subclass if unbound_method && unbound_method.owner.ancestors.first != unbound_method.owner # This is a singleton method; we can't do anything with it # But we can work around this using a different implementation double = method_double_from_ancestor_for(message) return object.method(double.method_stasher.stashed_method_name) end end raise # :nocov: end protected def original_unbound_method_handle_from_ancestor_for(message) double = method_double_from_ancestor_for(message) double && double.original_method.unbind end def method_double_from_ancestor_for(message) @method_doubles.fetch(message) do # The fact that there is no method double for this message indicates # that it has not been redefined by rspec-mocks. We need to continue # looking up the ancestor chain. return superclass_proxy && superclass_proxy.method_double_from_ancestor_for(message) end end def superclass_proxy return @superclass_proxy if defined?(@superclass_proxy) if (superclass = object.superclass) @superclass_proxy = @source_space.superclass_proxy_for(superclass) else @superclass_proxy = nil end end end # @private class PartialClassDoubleProxy < PartialDoubleProxy include PartialClassDoubleProxyMethods end # @private class ProxyForNil < PartialDoubleProxy def initialize(order_group) set_expectation_behavior super(nil, order_group) end attr_accessor :disallow_expectations attr_accessor :warn_about_expectations def add_message_expectation(method_name, opts={}, &block) warn_or_raise!(method_name) super end def add_stub(method_name, opts={}, &implementation) warn_or_raise!(method_name) super end private def set_expectation_behavior case RSpec::Mocks.configuration.allow_message_expectations_on_nil when false @warn_about_expectations = false @disallow_expectations = true when true @warn_about_expectations = false @disallow_expectations = false else @warn_about_expectations = true @disallow_expectations = false end end def warn_or_raise!(method_name) # This method intentionally swallows the message when # neither disallow_expectations nor warn_about_expectations # are set to true. if disallow_expectations raise_error(method_name) elsif warn_about_expectations warn(method_name) end end def warn(method_name) warning_msg = @error_generator.expectation_on_nil_message(method_name) RSpec.warning(warning_msg) end def raise_error(method_name) @error_generator.raise_expectation_on_nil_error(method_name) end end end end rspec-mocks-3.13.0/lib/rspec/mocks/space.rb000066400000000000000000000153131455767030500204610ustar00rootroot00000000000000RSpec::Support.require_rspec_support 'reentrant_mutex' module RSpec module Mocks # @private # Provides a default space implementation for outside # the scope of an example. Called "root" because it serves # as the root of the space stack. class RootSpace def proxy_for(*_args) raise_lifecycle_message end def any_instance_recorder_for(*_args) raise_lifecycle_message end def any_instance_proxy_for(*_args) raise_lifecycle_message end def register_constant_mutator(_mutator) raise_lifecycle_message end def any_instance_recorders_from_ancestry_of(_object) raise_lifecycle_message end def reset_all end def verify_all end def registered?(_object) false end def superclass_proxy_for(*_args) raise_lifecycle_message end def new_scope Space.new end private def raise_lifecycle_message raise OutsideOfExampleError, "The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported." end end # @private class Space attr_reader :proxies, :any_instance_recorders, :proxy_mutex, :any_instance_mutex def initialize @proxies = {} @any_instance_recorders = {} @constant_mutators = [] @expectation_ordering = OrderGroup.new @proxy_mutex = new_mutex @any_instance_mutex = new_mutex end def new_scope NestedSpace.new(self) end def verify_all proxies.values.each { |proxy| proxy.verify } any_instance_recorders.each_value { |recorder| recorder.verify } end def reset_all proxies.each_value { |proxy| proxy.reset } any_instance_recorders.each_value { |recorder| recorder.stop_all_observation! } any_instance_recorders.clear @constant_mutators.reverse.each { |mut| mut.idempotently_reset } end def register_constant_mutator(mutator) @constant_mutators << mutator end def constant_mutator_for(name) @constant_mutators.find { |m| m.full_constant_name == name } end def any_instance_recorder_for(klass, only_return_existing=false) any_instance_mutex.synchronize do id = klass.__id__ any_instance_recorders.fetch(id) do return nil if only_return_existing any_instance_recorder_not_found_for(id, klass) end end end def any_instance_proxy_for(klass) AnyInstance::Proxy.new(any_instance_recorder_for(klass), proxies_of(klass)) end def proxies_of(klass) proxies.values.select { |proxy| klass === proxy.object } end def proxy_for(object) proxy_mutex.synchronize do id = id_for(object) proxies.fetch(id) { proxy_not_found_for(id, object) } end end def superclass_proxy_for(klass) proxy_mutex.synchronize do id = id_for(klass) proxies.fetch(id) { superclass_proxy_not_found_for(id, klass) } end end alias ensure_registered proxy_for def registered?(object) proxies.key?(id_for object) end def any_instance_recorders_from_ancestry_of(object) # Optimization: `any_instance` is a feature we generally # recommend not using, so we can often early exit here # without doing an O(N) linear search over the number of # ancestors in the object's class hierarchy. return [] if any_instance_recorders.empty? # We access the ancestors through the singleton class, to avoid calling # `class` in case `class` has been stubbed. (class << object; ancestors; end).map do |klass| any_instance_recorders[klass.__id__] end.compact end private def new_mutex Support::ReentrantMutex.new end def proxy_not_found_for(id, object) proxies[id] = case object when NilClass then ProxyForNil.new(@expectation_ordering) when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering) when Class class_proxy_with_callback_verification_strategy(object, CallbackInvocationStrategy.new) else if RSpec::Mocks.configuration.verify_partial_doubles? VerifyingPartialDoubleProxy.new(object, @expectation_ordering) else PartialDoubleProxy.new(object, @expectation_ordering) end end end def superclass_proxy_not_found_for(id, object) raise "superclass_proxy_not_found_for called with something that is not a class" unless Class === object proxies[id] = class_proxy_with_callback_verification_strategy(object, NoCallbackInvocationStrategy.new) end def class_proxy_with_callback_verification_strategy(object, strategy) if RSpec::Mocks.configuration.verify_partial_doubles? VerifyingPartialClassDoubleProxy.new( self, object, @expectation_ordering, strategy ) else PartialClassDoubleProxy.new(self, object, @expectation_ordering) end end def any_instance_recorder_not_found_for(id, klass) any_instance_recorders[id] = AnyInstance::Recorder.new(klass) end if defined?(::BasicObject) && !::BasicObject.method_defined?(:__id__) # for 1.9.2 require 'securerandom' def id_for(object) id = object.__id__ return id if object.equal?(::ObjectSpace._id2ref(id)) # this suggests that object.__id__ is proxying through to some wrapped object object.instance_exec do @__id_for_rspec_mocks_space ||= ::SecureRandom.uuid end end else def id_for(object) object.__id__ end end end # @private class NestedSpace < Space def initialize(parent) @parent = parent super() end def proxies_of(klass) super + @parent.proxies_of(klass) end def constant_mutator_for(name) super || @parent.constant_mutator_for(name) end def registered?(object) super || @parent.registered?(object) end private def proxy_not_found_for(id, object) @parent.proxies[id] || super end def any_instance_recorder_not_found_for(id, klass) @parent.any_instance_recorders[id] || super end end end end rspec-mocks-3.13.0/lib/rspec/mocks/standalone.rb000066400000000000000000000001151455767030500215100ustar00rootroot00000000000000require 'rspec/mocks' extend RSpec::Mocks::ExampleMethods RSpec::Mocks.setup rspec-mocks-3.13.0/lib/rspec/mocks/syntax.rb000066400000000000000000000267001455767030500207160ustar00rootroot00000000000000module RSpec module Mocks # @api private # Provides methods for enabling and disabling the available syntaxes # provided by rspec-mocks. module Syntax # @private def self.warn_about_should! @warn_about_should = true end # @private def self.warn_unless_should_configured(method_name , replacement="the new `:expect` syntax or explicitly enable `:should`") if @warn_about_should RSpec.deprecate( "Using `#{method_name}` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax", :replacement => replacement ) @warn_about_should = false end end # @api private # Enables the should syntax (`dbl.stub`, `dbl.should_receive`, etc). def self.enable_should(syntax_host=default_should_syntax_host) @warn_about_should = false if syntax_host == default_should_syntax_host return if should_enabled?(syntax_host) syntax_host.class_exec do def should_receive(message, opts={}, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) ::RSpec::Mocks.expect_message(self, message, opts, &block) end def should_not_receive(message, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) ::RSpec::Mocks.expect_message(self, message, {}, &block).never end def stub(message_or_hash, opts={}, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) if ::Hash === message_or_hash message_or_hash.each { |message, value| stub(message).and_return value } else ::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block) end end def unstub(message) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to receive(...).and_call_original` or explicitly enable `:should`") ::RSpec::Mocks.space.proxy_for(self).remove_stub(message) end def stub_chain(*chain, &blk) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) ::RSpec::Mocks::StubChain.stub_chain_on(self, *chain, &blk) end def as_null_object ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) @_null_object = true ::RSpec::Mocks.space.proxy_for(self).as_null_object end def null_object? ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) defined?(@_null_object) end def received_message?(message, *args, &block) ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) ::RSpec::Mocks.space.proxy_for(self).received_message?(message, *args, &block) end unless Class.respond_to? :any_instance Class.class_exec do def any_instance ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__) ::RSpec::Mocks.space.any_instance_proxy_for(self) end end end end end # @api private # Disables the should syntax (`dbl.stub`, `dbl.should_receive`, etc). def self.disable_should(syntax_host=default_should_syntax_host) return unless should_enabled?(syntax_host) syntax_host.class_exec do undef should_receive undef should_not_receive undef stub undef unstub undef stub_chain undef as_null_object undef null_object? undef received_message? end Class.class_exec do undef any_instance end end # @api private # Enables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc). def self.enable_expect(syntax_host=::RSpec::Mocks::ExampleMethods) return if expect_enabled?(syntax_host) syntax_host.class_exec do def receive(method_name, &block) Matchers::Receive.new(method_name, block) end def receive_messages(message_return_value_hash) matcher = Matchers::ReceiveMessages.new(message_return_value_hash) matcher.warn_about_block if block_given? matcher end def receive_message_chain(*messages, &block) Matchers::ReceiveMessageChain.new(messages, &block) end def allow(target) AllowanceTarget.new(target) end def expect_any_instance_of(klass) AnyInstanceExpectationTarget.new(klass) end def allow_any_instance_of(klass) AnyInstanceAllowanceTarget.new(klass) end end RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do def expect(target) ExpectationTarget.new(target) end end end # @api private # Disables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc). def self.disable_expect(syntax_host=::RSpec::Mocks::ExampleMethods) return unless expect_enabled?(syntax_host) syntax_host.class_exec do undef receive undef receive_messages undef receive_message_chain undef allow undef expect_any_instance_of undef allow_any_instance_of end RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do undef expect end end # @api private # Indicates whether or not the should syntax is enabled. def self.should_enabled?(syntax_host=default_should_syntax_host) syntax_host.method_defined?(:should_receive) end # @api private # Indicates whether or not the expect syntax is enabled. def self.expect_enabled?(syntax_host=::RSpec::Mocks::ExampleMethods) syntax_host.method_defined?(:allow) end # @api private # Determines where the methods like `should_receive`, and `stub` are added. def self.default_should_syntax_host # JRuby 1.7.4 introduces a regression whereby `defined?(::BasicObject) => nil` # yet `BasicObject` still exists and patching onto ::Object breaks things # e.g. SimpleDelegator expectations won't work # # See: https://github.com/jruby/jruby/issues/814 if defined?(JRUBY_VERSION) && JRUBY_VERSION == '1.7.4' && RUBY_VERSION.to_f > 1.8 return ::BasicObject end # On 1.8.7, Object.ancestors.last == Kernel but # things blow up if we include `RSpec::Mocks::Methods` # into Kernel...not sure why. return Object unless defined?(::BasicObject) # MacRuby has BasicObject but it's not the root class. return Object unless Object.ancestors.last == ::BasicObject ::BasicObject end end end end if defined?(BasicObject) # The legacy `:should` syntax adds the following methods directly to # `BasicObject` so that they are available off of any object. Note, however, # that this syntax does not always play nice with delegate/proxy objects. # We recommend you use the non-monkeypatching `:expect` syntax instead. # @see Class class BasicObject # @method should_receive # Sets an expectation that this object should receive a message before # the end of the example. # # @example # logger = double('logger') # thing_that_logs = ThingThatLogs.new(logger) # logger.should_receive(:log) # thing_that_logs.do_something_that_logs_a_message # # @note This is only available when you have enabled the `should` syntax. # @see RSpec::Mocks::ExampleMethods#expect # @method should_not_receive # Sets and expectation that this object should _not_ receive a message # during this example. # @see RSpec::Mocks::ExampleMethods#expect # @method stub # Tells the object to respond to the message with the specified value. # # @example # counter.stub(:count).and_return(37) # counter.stub(:count => 37) # counter.stub(:count) { 37 } # # @note This is only available when you have enabled the `should` syntax. # @see RSpec::Mocks::ExampleMethods#allow # @method unstub # Removes a stub. On a double, the object will no longer respond to # `message`. On a real object, the original method (if it exists) is # restored. # # This is rarely used, but can be useful when a stub is set up during a # shared `before` hook for the common case, but you want to replace it # for a special case. # # @note This is only available when you have enabled the `should` syntax. # @method stub_chain # @overload stub_chain(method1, method2) # @overload stub_chain("method1.method2") # @overload stub_chain(method1, method_to_value_hash) # # Stubs a chain of methods. # # ## Warning: # # Chains can be arbitrarily long, which makes it quite painless to # violate the Law of Demeter in violent ways, so you should consider any # use of `stub_chain` a code smell. Even though not all code smells # indicate real problems (think fluent interfaces), `stub_chain` still # results in brittle examples. For example, if you write # `foo.stub_chain(:bar, :baz => 37)` in a spec and then the # implementation calls `foo.baz.bar`, the stub will not work. # # @example # double.stub_chain("foo.bar") { :baz } # double.stub_chain(:foo, :bar => :baz) # double.stub_chain(:foo, :bar) { :baz } # # # Given any of ^^ these three forms ^^: # double.foo.bar # => :baz # # # Common use in Rails/ActiveRecord: # Article.stub_chain("recent.published") { [Article.new] } # # @note This is only available when you have enabled the `should` syntax. # @see RSpec::Mocks::ExampleMethods#receive_message_chain # @method as_null_object # Tells the object to respond to all messages. If specific stub values # are declared, they'll work as expected. If not, the receiver is # returned. # # @note This is only available when you have enabled the `should` syntax. # @method null_object? # Returns true if this object has received `as_null_object` # # @note This is only available when you have enabled the `should` syntax. end end # The legacy `:should` syntax adds the `any_instance` to `Class`. # We generally recommend you use the newer `:expect` syntax instead, # which allows you to stub any instance of a class using # `allow_any_instance_of(klass)` or mock any instance using # `expect_any_instance_of(klass)`. # @see BasicObject class Class # @method any_instance # Used to set stubs and message expectations on any instance of a given # class. Returns a [Recorder](Recorder), which records messages like # `stub` and `should_receive` for later playback on instances of the # class. # # @example # Car.any_instance.should_receive(:go) # race = Race.new # race.cars << Car.new # race.go # assuming this delegates to all of its cars # # this example would pass # # Account.any_instance.stub(:balance) { Money.new(:USD, 25) } # Account.new.balance # => Money.new(:USD, 25)) # # @return [Recorder] # # @note This is only available when you have enabled the `should` syntax. # @see RSpec::Mocks::ExampleMethods#expect_any_instance_of # @see RSpec::Mocks::ExampleMethods#allow_any_instance_of end rspec-mocks-3.13.0/lib/rspec/mocks/targets.rb000066400000000000000000000066001455767030500210360ustar00rootroot00000000000000module RSpec module Mocks # @private module TargetDelegationClassMethods def delegate_to(matcher_method) define_method(:to) do |matcher, &block| unless matcher_allowed?(matcher) raise_unsupported_matcher(:to, matcher) end define_matcher(matcher, matcher_method, &block) end end def delegate_not_to(matcher_method, options={}) method_name = options.fetch(:from) define_method(method_name) do |matcher, &block| case matcher when Matchers::Receive, Matchers::HaveReceived define_matcher(matcher, matcher_method, &block) when Matchers::ReceiveMessages, Matchers::ReceiveMessageChain raise_negation_unsupported(method_name, matcher) else raise_unsupported_matcher(method_name, matcher) end end end def disallow_negation(method_name) define_method(method_name) do |matcher, *_args| raise_negation_unsupported(method_name, matcher) end end end # @private module TargetDelegationInstanceMethods attr_reader :target private def matcher_allowed?(matcher) Matchers::Matcher === matcher end def define_matcher(matcher, name, &block) matcher.__send__(name, target, &block) end def raise_unsupported_matcher(method_name, matcher) raise UnsupportedMatcherError, "only the `receive`, `have_received` and `receive_messages` matchers are supported " \ "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}" end def raise_negation_unsupported(method_name, matcher) raise NegationUnsupportedError, "`#{expression}(...).#{method_name} #{matcher.matcher_name}` is not supported since it " \ "doesn't really make sense. What would it even mean?" end end # @private class TargetBase def initialize(target) @target = target end extend TargetDelegationClassMethods include TargetDelegationInstanceMethods end # @private module ExpectationTargetMethods extend TargetDelegationClassMethods include TargetDelegationInstanceMethods delegate_to :setup_expectation delegate_not_to :setup_negative_expectation, :from => :not_to delegate_not_to :setup_negative_expectation, :from => :to_not def expression :expect end end # @private class ExpectationTarget < TargetBase include ExpectationTargetMethods end # @private class AllowanceTarget < TargetBase def expression :allow end delegate_to :setup_allowance disallow_negation :not_to disallow_negation :to_not end # @private class AnyInstanceAllowanceTarget < TargetBase def expression :allow_any_instance_of end delegate_to :setup_any_instance_allowance disallow_negation :not_to disallow_negation :to_not end # @private class AnyInstanceExpectationTarget < TargetBase def expression :expect_any_instance_of end delegate_to :setup_any_instance_expectation delegate_not_to :setup_any_instance_negative_expectation, :from => :not_to delegate_not_to :setup_any_instance_negative_expectation, :from => :to_not end end end rspec-mocks-3.13.0/lib/rspec/mocks/test_double.rb000066400000000000000000000117661455767030500217070ustar00rootroot00000000000000module RSpec module Mocks # Implements the methods needed for a pure test double. RSpec::Mocks::Double # includes this module, and it is provided for cases where you want a # pure test double without subclassing RSpec::Mocks::Double. module TestDouble # Creates a new test double with a `name` (that will be used in error # messages only) def initialize(name=nil, stubs={}) @__expired = false if Hash === name && stubs.empty? stubs = name @name = nil else @name = name end assign_stubs(stubs) end # Tells the object to respond to all messages. If specific stub values # are declared, they'll work as expected. If not, the receiver is # returned. def as_null_object __mock_proxy.as_null_object end # Returns true if this object has received `as_null_object` def null_object? __mock_proxy.null_object? end # This allows for comparing the mock to other objects that proxy such as # ActiveRecords belongs_to proxy objects. By making the other object run # the comparison, we're sure the call gets delegated to the proxy # target. def ==(other) other == __mock_proxy end # @private def inspect TestDoubleFormatter.format(self) end # @private def to_s inspect.tr('<', '[').tr('>', ']') end # @private def respond_to?(message, incl_private=false) __mock_proxy.null_object? ? true : super end # @private def __build_mock_proxy_unless_expired(order_group) __raise_expired_error || __build_mock_proxy(order_group) end # @private def __disallow_further_usage! @__expired = true end # Override for default freeze implementation to prevent freezing of test # doubles. def freeze RSpec.warn_with("WARNING: you attempted to freeze a test double. This is explicitly a no-op as freezing doubles can lead to undesired behaviour when resetting tests.") self end private def method_missing(message, *args, &block) proxy = __mock_proxy proxy.record_message_received(message, *args, &block) if proxy.null_object? case message when :to_int then return 0 when :to_a, :to_ary then return nil when :to_str then return to_s else return self end end # Defined private and protected methods will still trigger `method_missing` # when called publicly. We want ruby's method visibility error to get raised, # so we simply delegate to `super` in that case. # ...well, we would delegate to `super`, but there's a JRuby # bug, so we raise our own visibility error instead: # https://github.com/jruby/jruby/issues/1398 visibility = proxy.visibility_for(message) if visibility == :private || visibility == :protected ErrorGenerator.new(self).raise_non_public_error( message, visibility ) end # Required wrapping doubles in an Array on Ruby 1.9.2 raise NoMethodError if [:to_a, :to_ary].include? message proxy.raise_unexpected_message_error(message, args) end def assign_stubs(stubs) stubs.each_pair do |message, response| __mock_proxy.add_simple_stub(message, response) end end def __mock_proxy ::RSpec::Mocks.space.proxy_for(self) end def __build_mock_proxy(order_group) TestDoubleProxy.new(self, order_group) end def __raise_expired_error return false unless @__expired ErrorGenerator.new(self).raise_expired_test_double_error end def initialize_copy(other) as_null_object if other.null_object? super end end # A generic test double object. `double`, `instance_double` and friends # return an instance of this. class Double include TestDouble end # @private module TestDoubleFormatter def self.format(dbl, unwrap=false) format = "#{type_desc(dbl)}#{verified_module_desc(dbl)} #{name_desc(dbl)}" return format if unwrap "#<#{format}>" end class << self private def type_desc(dbl) case dbl when InstanceVerifyingDouble then "InstanceDouble" when ClassVerifyingDouble then "ClassDouble" when ObjectVerifyingDouble then "ObjectDouble" else "Double" end end # @private IVAR_GET = Object.instance_method(:instance_variable_get) def verified_module_desc(dbl) return nil unless VerifyingDouble === dbl "(#{IVAR_GET.bind(dbl).call(:@doubled_module).description})" end def name_desc(dbl) return "(anonymous)" unless (name = IVAR_GET.bind(dbl).call(:@name)) name.inspect end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/verifying_double.rb000066400000000000000000000067071455767030500227310ustar00rootroot00000000000000RSpec::Support.require_rspec_mocks 'verifying_proxy' module RSpec module Mocks # @private module VerifyingDouble def respond_to?(message, include_private=false) return super unless null_object? method_ref = __mock_proxy.method_reference[message] case method_ref.visibility when :public then true when :private then include_private when :protected then include_private || RUBY_VERSION.to_f < 2.0 else !method_ref.unimplemented? end end def method_missing(message, *args, &block) # Null object conditional is an optimization. If not a null object, # validity of method expectations will have been checked at definition # time. if null_object? if @__sending_message == message __mock_proxy.ensure_implemented(message) else __mock_proxy.ensure_publicly_implemented(message, self) end __mock_proxy.validate_arguments!(message, args) end super end # Redefining `__send__` causes ruby to issue a warning. old, $VERBOSE = $VERBOSE, nil def __send__(name, *args, &block) @__sending_message = name super ensure @__sending_message = nil end ruby2_keywords :__send__ if respond_to?(:ruby2_keywords, true) $VERBOSE = old def send(name, *args, &block) __send__(name, *args, &block) end ruby2_keywords :send if respond_to?(:ruby2_keywords, true) def initialize(doubled_module, *args) @doubled_module = doubled_module possible_name = args.first name = if String === possible_name || Symbol === possible_name args.shift end super(name, *args) @__sending_message = nil end end # A mock providing a custom proxy that can verify the validity of any # method stubs or expectations against the public instance methods of the # given class. # # @private class InstanceVerifyingDouble include TestDouble include VerifyingDouble def __build_mock_proxy(order_group) VerifyingProxy.new(self, order_group, @doubled_module, InstanceMethodReference ) end end # An awkward module necessary because we cannot otherwise have # ClassVerifyingDouble inherit from Module and still share these methods. # # @private module ObjectVerifyingDoubleMethods include TestDouble include VerifyingDouble def as_stubbed_const(options={}) ConstantMutator.stub(@doubled_module.const_to_replace, self, options) self end private def __build_mock_proxy(order_group) VerifyingProxy.new(self, order_group, @doubled_module, ObjectMethodReference ) end end # Similar to an InstanceVerifyingDouble, except that it verifies against # public methods of the given object. # # @private class ObjectVerifyingDouble include ObjectVerifyingDoubleMethods end # Effectively the same as an ObjectVerifyingDouble (since a class is a type # of object), except with Module in the inheritance chain so that # transferring nested constants to work. # # @private class ClassVerifyingDouble < Module include ObjectVerifyingDoubleMethods end end end rspec-mocks-3.13.0/lib/rspec/mocks/verifying_message_expectation.rb000066400000000000000000000037041455767030500255000ustar00rootroot00000000000000RSpec::Support.require_rspec_support 'method_signature_verifier' module RSpec module Mocks # A message expectation that knows about the real implementation of the # message being expected, so that it can verify that any expectations # have the valid arguments. # @api private class VerifyingMessageExpectation < MessageExpectation # A level of indirection is used here rather than just passing in the # method itself, since method look up is expensive and we only want to # do it if actually needed. # # Conceptually the method reference makes more sense as a constructor # argument since it should be immutable, but it is significantly more # straight forward to build the object in pieces so for now it stays as # an accessor. attr_accessor :method_reference def initialize(*args) super end # @private def with(*args, &block) super(*args, &block).tap do validate_expected_arguments! do |signature| example_call_site_args = [:an_arg] * signature.min_non_kw_args example_call_site_args << :kw_args_hash if signature.required_kw_args.any? @argument_list_matcher.resolve_expected_args_based_on(example_call_site_args) end end end ruby2_keywords(:with) if respond_to?(:ruby2_keywords, true) private def validate_expected_arguments! return if method_reference.nil? method_reference.with_signature do |signature| args = yield signature verifier = Support::LooseSignatureVerifier.new(signature, args) unless verifier.valid? # Fail fast is required, otherwise the message expectation will fail # as well ("expected method not called") and clobber this one. @failed_fast = true @error_generator.raise_invalid_arguments_error(verifier) end end end end end end rspec-mocks-3.13.0/lib/rspec/mocks/verifying_proxy.rb000066400000000000000000000162041455767030500226310ustar00rootroot00000000000000RSpec::Support.require_rspec_mocks 'verifying_message_expectation' RSpec::Support.require_rspec_mocks 'method_reference' module RSpec module Mocks # @private class CallbackInvocationStrategy def call(doubled_module) RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| block.call doubled_module end end end # @private class NoCallbackInvocationStrategy def call(_doubled_module) end end # @private module VerifyingProxyMethods def add_stub(method_name, opts={}, &implementation) ensure_implemented(method_name) super end def add_simple_stub(method_name, *args) ensure_implemented(method_name) super end def add_message_expectation(method_name, opts={}, &block) ensure_implemented(method_name) super end def ensure_implemented(method_name) return unless method_reference[method_name].unimplemented? @error_generator.raise_unimplemented_error( @doubled_module, method_name, @object ) end def ensure_publicly_implemented(method_name, _object) ensure_implemented(method_name) visibility = method_reference[method_name].visibility return if visibility == :public @error_generator.raise_non_public_error(method_name, visibility) end end # A verifying proxy mostly acts like a normal proxy, except that it # contains extra logic to try and determine the validity of any expectation # set on it. This includes whether or not methods have been defined and the # validity of arguments on method calls. # # In all other ways this behaves like a normal proxy. It only adds the # verification behaviour to specific methods then delegates to the parent # implementation. # # These checks are only activated if the doubled class has already been # loaded, otherwise they are disabled. This allows for testing in # isolation. # # @private class VerifyingProxy < TestDoubleProxy include VerifyingProxyMethods def initialize(object, order_group, doubled_module, method_reference_class) super(object, order_group) @object = object @doubled_module = doubled_module @method_reference_class = method_reference_class # A custom method double is required to pass through a way to lookup # methods to determine their parameters. This is only relevant if the doubled # class is loaded. @method_doubles = Hash.new do |h, k| h[k] = VerifyingMethodDouble.new(@object, k, self, method_reference[k]) end end def method_reference @method_reference ||= Hash.new do |h, k| h[k] = @method_reference_class.for(@doubled_module, k) end end def visibility_for(method_name) method_reference[method_name].visibility end def validate_arguments!(method_name, args) @method_doubles[method_name].validate_arguments!(args) end end # @private DEFAULT_CALLBACK_INVOCATION_STRATEGY = CallbackInvocationStrategy.new # @private class VerifyingPartialDoubleProxy < PartialDoubleProxy include VerifyingProxyMethods def initialize(object, expectation_ordering, optional_callback_invocation_strategy=DEFAULT_CALLBACK_INVOCATION_STRATEGY) super(object, expectation_ordering) @doubled_module = DirectObjectReference.new(object) # A custom method double is required to pass through a way to lookup # methods to determine their parameters. @method_doubles = Hash.new do |h, k| h[k] = VerifyingExistingMethodDouble.for(object, k, self) end optional_callback_invocation_strategy.call(@doubled_module) end def ensure_implemented(_method_name) return if Mocks.configuration.temporarily_suppress_partial_double_verification super end def method_reference @method_doubles end end # @private class VerifyingPartialClassDoubleProxy < VerifyingPartialDoubleProxy include PartialClassDoubleProxyMethods end # @private class VerifyingMethodDouble < MethodDouble def initialize(object, method_name, proxy, method_reference) super(object, method_name, proxy) @method_reference = method_reference end def message_expectation_class VerifyingMessageExpectation end def add_expectation(*args, &block) # explicit params necessary for 1.8.7 see #626 super(*args, &block).tap { |x| x.method_reference = @method_reference } end def add_stub(*args, &block) # explicit params necessary for 1.8.7 see #626 super(*args, &block).tap { |x| x.method_reference = @method_reference } end def proxy_method_invoked(obj, *args, &block) validate_arguments!(args) super end ruby2_keywords :proxy_method_invoked if respond_to?(:ruby2_keywords, true) def validate_arguments!(actual_args) @method_reference.with_signature do |signature| verifier = Support::StrictSignatureVerifier.new(signature, actual_args) raise ArgumentError, verifier.error_message unless verifier.valid? end end end # A VerifyingMethodDouble fetches the method to verify against from the # original object, using a MethodReference. This works for pure doubles, # but when the original object is itself the one being modified we need to # collapse the reference and the method double into a single object so that # we can access the original pristine method definition. # # @private class VerifyingExistingMethodDouble < VerifyingMethodDouble def initialize(object, method_name, proxy) super(object, method_name, proxy, self) @valid_method = object.respond_to?(method_name, true) # Trigger an eager find of the original method since if we find it any # later we end up getting a stubbed method with incorrect arity. save_original_implementation_callable! end def with_signature yield Support::MethodSignature.new(original_implementation_callable) end def unimplemented? !@valid_method end def self.for(object, method_name, proxy) if ClassNewMethodReference.applies_to?(method_name) { object } VerifyingExistingClassNewMethodDouble elsif Mocks.configuration.temporarily_suppress_partial_double_verification MethodDouble else self end.new(object, method_name, proxy) end end # Used in place of a `VerifyingExistingMethodDouble` for the specific case # of mocking or stubbing a `new` method on a class. In this case, we substitute # the method signature from `#initialize` since new's signature is just `*args`. # # @private class VerifyingExistingClassNewMethodDouble < VerifyingExistingMethodDouble def with_signature yield Support::MethodSignature.new(object.instance_method(:initialize)) end end end end rspec-mocks-3.13.0/lib/rspec/mocks/version.rb000066400000000000000000000003061455767030500210470ustar00rootroot00000000000000module RSpec module Mocks # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. STRING = '3.13.0' end end end rspec-mocks-3.13.0/maintenance-branch000066400000000000000000000000211455767030500174710ustar00rootroot000000000000003-13-maintenance rspec-mocks-3.13.0/rspec-mocks.gemspec000066400000000000000000000043161455767030500176370ustar00rootroot00000000000000# -*- encoding: utf-8 -*- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__) require "rspec/mocks/version" Gem::Specification.new do |s| s.name = "rspec-mocks" s.version = RSpec::Mocks::Version::STRING s.platform = Gem::Platform::RUBY s.license = "MIT" s.authors = ["Steven Baker", "David Chelimsky", "Myron Marston"] s.email = "rspec@googlegroups.com" s.homepage = "https://github.com/rspec/rspec-mocks" s.summary = "rspec-mocks-#{RSpec::Mocks::Version::STRING}" s.description = "RSpec's 'test double' framework, with support for stubbing and mocking" s.metadata = { 'bug_tracker_uri' => 'https://github.com/rspec/rspec-mocks/issues', 'changelog_uri' => "https://github.com/rspec/rspec-mocks/blob/v#{s.version}/Changelog.md", 'documentation_uri' => 'https://rspec.info/documentation/', 'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/rspec', 'source_code_uri' => 'https://github.com/rspec/rspec-mocks', } s.files = `git ls-files -- lib/*`.split("\n") s.files += %w[README.md LICENSE.md Changelog.md .yardopts .document] s.test_files = [] s.rdoc_options = ["--charset=UTF-8"] s.require_path = "lib" s.required_ruby_version = '>= 1.8.7' private_key = File.expand_path('~/.gem/rspec-gem-private_key.pem') if File.exist?(private_key) s.signing_key = private_key s.cert_chain = [File.expand_path('~/.gem/rspec-gem-public_cert.pem')] end if RSpec::Mocks::Version::STRING =~ /[a-zA-Z]+/ # pin to exact version for rc's and betas s.add_runtime_dependency "rspec-support", "= #{RSpec::Mocks::Version::STRING}" else # pin to major/minor ignoring patch s.add_runtime_dependency "rspec-support", "~> #{RSpec::Mocks::Version::STRING.split('.')[0..1].concat(['0']).join('.')}" end s.add_runtime_dependency "diff-lcs", ">= 1.2.0", "< 2.0" s.add_development_dependency 'rake', '> 10.0.0' s.add_development_dependency 'cucumber', '>= 1.3' if RUBY_VERSION.to_f >= 2.4 s.add_development_dependency 'aruba', '>= 1.1.0', '< 3.0.0' else s.add_development_dependency 'aruba', '~> 0.14.10' end s.add_development_dependency 'minitest', '~> 5.2' end rspec-mocks-3.13.0/script/000077500000000000000000000000001455767030500153445ustar00rootroot00000000000000rspec-mocks-3.13.0/script/ci_functions.sh000066400000000000000000000037631455767030500203740ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: # https://github.com/travis-ci/travis-build/blob/e9314616e182a23e6a280199cd9070bfc7cae548/lib/travis/build/script/templates/header.sh#L34-L53 ci_retry() { local result=0 local count=1 while [ $count -le 3 ]; do [ $result -ne 0 ] && { echo -e "\n\033[33;1mThe command \"$@\" failed. Retrying, $count of 3.\033[0m\n" >&2 } "$@" result=$? [ $result -eq 0 ] && break count=$(($count + 1)) sleep 1 done [ $count -eq 3 ] && { echo "\n\033[33;1mThe command \"$@\" failed 3 times.\033[0m\n" >&2 } return $result } # Taken from https://github.com/vcr/vcr/commit/fa96819c92b783ec0c794f788183e170e4f684b2 # and https://github.com/vcr/vcr/commit/040aaac5370c68cd13c847c076749cd547a6f9b1 nano_cmd="$(type -p gdate date | head -1)" nano_format="+%s%N" [ "$(uname -s)" != "Darwin" ] || nano_format="${nano_format/%N/000000000}" travis_time_start() { travis_timer_id=$(printf %08x $(( RANDOM * RANDOM ))) travis_start_time=$($nano_cmd -u "$nano_format") printf "travis_time:start:%s\r\e[0m" $travis_timer_id } travis_time_finish() { local travis_end_time=$($nano_cmd -u "$nano_format") local duration=$(($travis_end_time-$travis_start_time)) printf "travis_time:end:%s:start=%s,finish=%s,duration=%s\r\e[0m" \ $travis_timer_id $travis_start_time $travis_end_time $duration } fold() { local name="$1" local status=0 shift 1 if [ -n "$TRAVIS" ]; then printf "travis_fold:start:%s\r\e[0m" "$name" travis_time_start else echo "============= Starting $name ===============" fi "$@" status=$? [ -z "$TRAVIS" ] || travis_time_finish if [ "$status" -eq 0 ]; then if [ -n "$TRAVIS" ]; then printf "travis_fold:end:%s\r\e[0m" "$name" else echo "============= Ending $name ===============" fi else STATUS="$status" fi return $status } rspec-mocks-3.13.0/script/clone_all_rspec_repos000077500000000000000000000011101455767030500216170ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh if is_mri; then pushd .. clone_repo "rspec" clone_repo "rspec-core" clone_repo "rspec-expectations" clone_repo "rspec-mocks" clone_repo "rspec-rails" "6-1-maintenance" if rspec_support_compatible; then clone_repo "rspec-support" fi popd else echo "Not cloning all repos since we are not on MRI and they are only needed for the MRI build" fi rspec-mocks-3.13.0/script/cucumber.sh000077500000000000000000000003421455767030500175070ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh run_cukes rspec-mocks-3.13.0/script/functions.sh000066400000000000000000000140401455767030500177070ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $SCRIPT_DIR/ci_functions.sh source $SCRIPT_DIR/predicate_functions.sh # If JRUBY_OPTS isn't set, use these. # see https://docs.travis-ci.com/user/ci-environment/ export JRUBY_OPTS=${JRUBY_OPTS:-"--server -Xcompile.invokedynamic=false"} SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` # Don't allow rubygems to pollute what's loaded. Also, things boot faster # without the extra load time of rubygems. Only works on MRI Ruby 1.9+ if is_mri_192_plus; then export RUBYOPT="--disable=gem" fi function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there if [ -z "$2" ]; then BRANCH_TO_CLONE="${MAINTENANCE_BRANCH?}"; else BRANCH_TO_CLONE="$2"; fi; ci_retry eval "git clone https://github.com/rspec/$1 --depth 1 --branch ${BRANCH_TO_CLONE?}" fi; } function run_specs_and_record_done { local rspec_bin=bin/rspec # rspec-core needs to run with a special script that loads simplecov first, # so that it can instrument rspec-core's code before rspec-core has been loaded. if [ -f script/rspec_with_simplecov ] && is_mri; then rspec_bin=script/rspec_with_simplecov fi; echo "${PWD}/bin/rspec" $rspec_bin spec --backtrace --format progress --profile --format progress --out $SPECS_HAVE_RUN_FILE } function run_cukes { if [ -d features ]; then # force jRuby to use client mode JVM or a compilation mode thats as close as possible, # idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time # # Note that we delay setting this until we run the cukes because we've seen # spec failures in our spec suite due to problems with this mode. export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' echo "${PWD}/bin/cucumber" if is_mri_192; then # For some reason we get SystemStackError on 1.9.2 when using # the bin/cucumber approach below. That approach is faster # (as it avoids the bundler tax), so we use it on rubies where we can. bundle exec cucumber --strict elif is_jruby; then # For some reason JRuby doesn't like our improved bundler setup RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ PATH="${PWD}/bin:$PATH" \ bin/cucumber --strict else # Prepare RUBYOPT for scenarios that are shelling out to ruby, # and PATH for those that are using `rspec` or `rake`. RUBYOPT="${RUBYOPT} -I${PWD}/../bundle -rbundler/setup" \ PATH="${PWD}/bin:$PATH" \ bin/cucumber --strict fi fi } function run_specs_one_by_one { echo "Running each spec file, one-by-one..." for file in `find spec -iname '*_spec.rb'`; do echo "Running $file" bin/rspec $file -b --format progress done } function run_spec_suite_for { if [ ! -f ../$1/$SPECS_HAVE_RUN_FILE ]; then # don't rerun specs that have already run if [ -d ../$1 ]; then echo "Running specs for $1" pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .github/workflows/ci.yml | grep "bundle install" | sed 's/.* bundle install//'` ci_retry eval "(unset RUBYOPT; exec bundle install $bundle_install_flags)" ci_retry eval "(unset RUBYOPT; exec bundle binstubs --all)" run_specs_and_record_done popd else echo "" echo "WARNING: The ../$1 directory does not exist. Usually the" echo "build cds into that directory and run the specs to ensure" echo "the specs still pass with your latest changes, but we are" echo "going to skip that step." echo "" fi; fi; } function check_binstubs { echo "Checking required binstubs" local success=0 local binstubs="" local gems="" if [ ! -x ./bin/rspec ]; then binstubs="$binstubs bin/rspec" gems="$gems rspec-core" success=1 fi if [ ! -x ./bin/rake ]; then binstubs="$binstubs bin/rake" gems="$gems rake" success=1 fi if [ -d features ]; then if [ ! -x ./bin/cucumber ]; then binstubs="$binstubs bin/cucumber" gems="$gems cucumber" success=1 fi fi if [ $success -eq 1 ]; then echo echo "Missing binstubs:$binstubs" echo "Install missing binstubs using one of the following:" echo echo " # Create the missing binstubs" echo " $ bundle binstubs$gems" echo echo " # To binstub all gems" echo " $ bundle binstubs --all" fi return $success } function check_documentation_coverage { echo "bin/yard stats --list-undoc" bin/yard stats --list-undoc | ruby -e " while line = gets has_warnings ||= line.start_with?('[warn]:') coverage ||= line[/([\d\.]+)% documented/, 1] puts line end unless Float(coverage) == 100 puts \"\n\nMissing documentation coverage (currently at #{coverage}%)\" exit(1) end if has_warnings puts \"\n\nYARD emitted documentation warnings.\" exit(1) end " # Some warnings only show up when generating docs, so do that as well. bin/yard doc --no-cache | ruby -e " while line = gets has_warnings ||= line.start_with?('[warn]:') has_errors ||= line.start_with?('[error]:') puts line end if has_warnings || has_errors puts \"\n\nYARD emitted documentation warnings or errors.\" exit(1) end " } function check_style_and_lint { echo "bin/rubocop lib" eval "(unset RUBYOPT; exec bin/rubocop lib)" } function run_all_spec_suites { fold "rspec-core specs" run_spec_suite_for "rspec-core" fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" if rspec_rails_compatible; then if ! is_ruby_27_plus; then export RAILS_VERSION='~> 6.1.0' fi fold "rspec-rails specs" run_spec_suite_for "rspec-rails" fi if rspec_support_compatible; then fold "rspec-support specs" run_spec_suite_for "rspec-support" fi } rspec-mocks-3.13.0/script/ignores000066400000000000000000000103301455767030500167320ustar00rootroot00000000000000# grep -v -f doesn't work properly when empty, so this line is here. # The `alias_method` calls below are only executed at file load time # (when the method cache will be busted by defining methods anyway). lib/rspec/mocks/argument_matchers.rb: alias_method :hash_not_including, :hash_excluding lib/rspec/mocks/argument_matchers.rb: alias_method :an_instance_of, :instance_of lib/rspec/mocks/argument_matchers.rb: alias_method :a_kind_of, :kind_of lib/rspec/mocks/method_double.rb: alias_method :save_original_method!, :original_method lib/rspec/mocks/test_double.rb: alias_method :to_str, :to_s lib/rspec/mocks/error_generator.rb: MockExpectationError = Class.new(Exception) lib/rspec/mocks/error_generator.rb: ExpiredTestDoubleError = Class.new(MockExpectationError) lib/rspec/mocks/error_generator.rb: OutsideOfExampleError = Class.new(StandardError) lib/rspec/mocks/error_generator.rb: UnsupportedMatcherError = Class.new(StandardError) lib/rspec/mocks/error_generator.rb: NegationUnsupportedError = Class.new(StandardError) lib/rspec/mocks/message_expectation.rb: CannotModifyFurtherError = Class.new(StandardError) lib/rspec/mocks/mutate_const.rb: extend RecursiveConstMethods lib/rspec/mocks/mutate_const.rb: extend RecursiveConstMethods # These calls are explicitly opt-in, probably at configuration time. lib/rspec/mocks/marshal_extension.rb: alias_method :dump_without_rspec_mocks, :dump lib/rspec/mocks/marshal_extension.rb: alias_method :dump, :dump_with_rspec_mocks lib/rspec/mocks/marshal_extension.rb: alias_method :dump, :dump_without_rspec_mocks # False positives due to naming lib/rspec/mocks/any_instance/recorder.rb: def build_alias_method_name(method_name) lib/rspec/mocks/any_instance/recorder.rb: if public_protected_or_private_method_defined?(build_alias_method_name(method_name)) lib/rspec/mocks/any_instance/recorder.rb: alias_method_name = build_alias_method_name(method_name) lib/rspec/mocks/any_instance/recorder.rb: alias_method_name = build_alias_method_name(method_name) lib/rspec/mocks/proxy.rb: build_alias_method_name(message) # Instance method stashing needs to blow away method cache, no way around it. lib/rspec/mocks/any_instance/recorder.rb: alias_method method_name, alias_method_name lib/rspec/mocks/any_instance/recorder.rb: alias_method alias_method_name, method_name lib/rspec/mocks/any_instance/recorder.rb: remove_method method_name lib/rspec/mocks/any_instance/recorder.rb: remove_method alias_method_name lib/rspec/mocks/any_instance/recorder.rb: remove_method method_name lib/rspec/mocks/instance_method_stasher.rb: @klass.__send__(:alias_method, stashed_method_name, @method) lib/rspec/mocks/instance_method_stasher.rb: @klass.__send__(:alias_method, @method, stashed_method_name) lib/rspec/mocks/instance_method_stasher.rb: @klass.__send__(:remove_method, stashed_method_name) # Constant stubbing needs to blow away method cache, no way around it. lib/rspec/mocks/method_double.rb: object_singleton_class.__send__(:remove_method, @method_name) lib/rspec/mocks/mutate_const.rb: @context.const_set(@const_name, @original_value) lib/rspec/mocks/mutate_const.rb: @context.const_set(@const_name, @mutated_value) lib/rspec/mocks/mutate_const.rb: @context.const_set(@const_name, @original_value) lib/rspec/mocks/mutate_const.rb: @mutated_value.const_set(const, get_const_defined_on(original_value, const)) lib/rspec/mocks/mutate_const.rb: klass.const_set(name, Module.new) lib/rspec/mocks/mutate_const.rb: context.const_set(@const_name, @mutated_value) lib/rspec/mocks/mutate_const.rb: @context.__send__(:remove_const, @const_name) lib/rspec/mocks/mutate_const.rb: @context.__send__(:remove_const, @const_name) lib/rspec/mocks/mutate_const.rb: @context.__send__(:remove_const, @const_name) lib/rspec/mocks/mutate_const.rb: @deepest_defined_const.__send__(:remove_const, @const_to_remove) # We provide our own wrapper around extend for others to use if they choose. lib/rspec/mocks/test_double.rb: object.extend self rspec-mocks-3.13.0/script/legacy_setup.sh000077500000000000000000000007721455767030500203750ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh bundle install --standalone --binstubs --without coverage documentation if [ -x ./bin/rspec ]; then echo "RSpec bin detected" else if [ -x ./exe/rspec ]; then cp ./exe/rspec ./bin/rspec echo "RSpec restored from exe" else echo "No RSpec bin available" exit 1 fi fi rspec-mocks-3.13.0/script/list_method_cache_busters.sh000077500000000000000000000023751455767030500231170ustar00rootroot00000000000000#!/bin/bash # set -x # This list is from https://charlie.bz/blog/things-that-clear-rubys-method-cache IGNORE_FILE=/tmp/cache_busters_ignore COMMENT_LINE_RE="^(\w|\/)+\.rb: +#" cat script/ignores | grep -v "^$" | ruby -ne 'puts $_.split(/\s+###/)[0]' > $IGNORE_FILE egrep 'def [a-z]*\..*' -R lib | grep -v "def self" | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" egrep 'undef\\b' -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep alias_method -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep remove_method -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep const_set -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep remove_const -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" egrep '\bextend\b' -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep 'Class.new' -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep private_constant -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep public_constant -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep "Marshal.load" -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep "OpenStruct.new" -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" rspec-mocks-3.13.0/script/predicate_functions.sh000066400000000000000000000055511455767030500217360ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { if ruby -e "exit(!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby')"; then # RUBY_ENGINE only returns 'ruby' on MRI. # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) return 0 else return 1 fi; } function is_ruby_head { # This checks for the presence of our CI's ruby-head env variable if [ -z ${RUBY_HEAD+x} ]; then return 1 else return 0 fi; } function supports_cross_build_checks { if is_mri; then # We don't run cross build checks on ruby-head if is_ruby_head; then return 1 else return 0 fi else return 1 fi } function is_jruby { if ruby -e "exit(defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java')"; then # RUBY_ENGINE only returns 'ruby' on MRI. # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) return 0 else return 1 fi; } function is_mri_192 { if is_mri; then if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then return 0 else return 1 fi else return 1 fi } function is_mri_192_plus { if is_mri; then if ruby -e "exit(RUBY_VERSION.to_f > 1.8)"; then return 0 else return 1 fi else return 1 fi } function is_mri_2plus { if is_mri; then if ruby -e "exit(RUBY_VERSION.to_f > 2.0)"; then return 0 else return 1 fi else return 1 fi } function is_ruby_23_plus { if ruby -e "exit(RUBY_VERSION.to_f >= 2.3)"; then return 0 else return 1 fi } function is_ruby_25_plus { if ruby -e "exit(RUBY_VERSION.to_f >= 2.5)"; then return 0 else return 1 fi } function is_ruby_27_plus { if ruby -e "exit(RUBY_VERSION.to_f >= 2.7)"; then return 0 else return 1 fi } function is_ruby_31_plus { if ruby -e "exit(RUBY_VERSION.to_f >= 3.1)"; then return 0 else return 1 fi } function rspec_rails_compatible { if is_ruby_25_plus; then # TODO remove when RSpec-Rails build is 3.1 safe by default if is_ruby_31_plus; then return 1 else return 0 fi else return 1 fi } function rspec_support_compatible { if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then return 0 else return 1 fi } function additional_specs_available { type run_additional_specs > /dev/null 2>&1 return $? } function documentation_enforced { if [ -x ./bin/yard ]; then if is_mri_2plus; then return 0 else return 1 fi else return 1 fi } function style_and_lint_enforced { if is_ruby_head; then return 1 else if [ -x ./bin/rubocop ]; then return 0 else return 1 fi fi } rspec-mocks-3.13.0/script/run_build000077500000000000000000000015171455767030500172610ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh # Allow repos to override the default functions and add their own if [ -f script/custom_build_functions.sh ]; then source script/custom_build_functions.sh fi fold "binstub check" check_binstubs fold "specs" run_specs_and_record_done if additional_specs_available; then fold "additional specs" run_additional_specs fi fold "cukes" run_cukes if documentation_enforced; then fold "doc check" check_documentation_coverage fi if supports_cross_build_checks; then fold "one-by-one specs" run_specs_one_by_one export NO_COVERAGE=true run_all_spec_suites else echo "Skipping the rest of the build on non-MRI rubies" fi rspec-mocks-3.13.0/script/run_rubocop000077500000000000000000000006361455767030500176340ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh # Allow repos to override the default functions and add their own if [ -f script/custom_build_functions.sh ]; then source script/custom_build_functions.sh fi fold "rubocop" check_style_and_lint rspec-mocks-3.13.0/script/update_rubygems_and_install_bundler000077500000000000000000000012251455767030500245540ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:07:49+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh if is_ruby_31_plus; then echo "Installing rubygems 3.3.6 / bundler 2.3.6" yes | gem update --system '3.3.6' yes | gem install bundler -v '2.3.6' elif is_ruby_23_plus; then echo "Installing rubygems 3.2.22 / bundler 2.2.22" yes | gem update --system '3.2.22' yes | gem install bundler -v '2.2.22' else echo "Warning installing older versions of Rubygems / Bundler" gem update --system '2.7.8' gem install bundler -v '1.17.3' fi rspec-mocks-3.13.0/spec/000077500000000000000000000000001455767030500147725ustar00rootroot00000000000000rspec-mocks-3.13.0/spec/integration/000077500000000000000000000000001455767030500173155ustar00rootroot00000000000000rspec-mocks-3.13.0/spec/integration/rails_support_spec.rb000066400000000000000000000031401455767030500235600ustar00rootroot00000000000000require File.expand_path('../../support/aruba', __FILE__) RSpec.describe "Supporting Rails monkey patches", :type => :aruba do before do if RSpec::Support::OS.windows? && RUBY_VERSION.to_f < 2.4 skip "Aruba on windows is broken on Ruby 2.3 and below" end end it "works when Rails has monkey patched #with" do write_file( "spec/with_monkey_patch_spec.rb", """ class Object # Rails monkey patches in **kwargs but this is a good analogy def with end end RSpec.describe do specify do mock = instance_double(\"Hash\") allow(mock).to receive(:key?).with(:x) { 1 } expect(mock.key?(:x)).to eq 1 end end """ ) run_command("bundle exec rspec spec/with_monkey_patch_spec.rb") expect(last_command_started).to have_output(/0 failures/) expect(last_command_started).to have_exit_status(0) end it "works mocking any instance when Rails has monkey patched #with" do write_file( "spec/with_monkey_patch_spec.rb", """ class Object # Rails monkey patches in **kwargs but this is a good analogy def with end end RSpec.describe do specify do klass = Class.new allow_any_instance_of(klass).to receive(:bar).with(:y) { 2 } expect(klass.new.bar(:y)).to eq 2 end end """ ) run_command("bundle exec rspec spec/with_monkey_patch_spec.rb") expect(last_command_started).to have_output(/0 failures/) expect(last_command_started).to have_exit_status(0) end end rspec-mocks-3.13.0/spec/rspec/000077500000000000000000000000001455767030500161065ustar00rootroot00000000000000rspec-mocks-3.13.0/spec/rspec/mocks/000077500000000000000000000000001455767030500172225ustar00rootroot00000000000000rspec-mocks-3.13.0/spec/rspec/mocks/and_call_original_spec.rb000066400000000000000000000314231455767030500242050ustar00rootroot00000000000000require 'delegate' RSpec.describe "and_call_original" do context "on a partial double" do let(:klass) do Class.new do def meth_1 :original end def meth_2(x) yield x, :additional_yielded_arg end if RSpec::Support::RubyFeatures.kw_args_supported? binding.eval(<<-RUBY, __FILE__, __LINE__) def meth_3(**kwargs) kwargs end def meth_4(x: 1) x end RUBY end if RSpec::Support::RubyFeatures.required_kw_args_supported? binding.eval(<<-RUBY, __FILE__, __LINE__) def meth_5(x:) x end RUBY end def self.new_instance new end end end let(:instance) { klass.new } context "when a method that exists has been stubbed previously" do before { allow(instance).to receive(:meth_1).and_return(:override) } it 'restores the original behavior' do expect { allow(instance).to receive(:meth_1).and_call_original }.to change(instance, :meth_1).from(:override).to(:original) end end context "when a non-existent method has been stubbed previously" do it 'restores the original NameError behavior' do expect { instance.abcd }.to raise_error(NameError).with_message(/abcd/) allow(instance).to receive(:abcd).and_return(:override) expect(instance.abcd).to eq(:override) allow(instance).to receive(:abcd).and_call_original expect { instance.abcd }.to raise_error(NameError).with_message(/abcd/) end end it 'passes the received message through to the original method' do expect(instance).to receive(:meth_1).and_call_original expect(instance.meth_1).to eq(:original) end it 'ignores prior declared stubs' do allow(instance).to receive(:meth_1).and_return(:stubbed_value) expect(instance).to receive(:meth_1).and_call_original expect(instance.meth_1).to eq(:original) end it 'passes args and blocks through to the original method' do expect(instance).to receive(:meth_2).and_call_original value = instance.meth_2(:submitted_arg) { |a, b| [a, b] } expect(value).to eq([:submitted_arg, :additional_yielded_arg]) end it 'errors when you pass through the wrong number of args' do expect(instance).to receive(:meth_1).and_call_original expect(instance).to receive(:meth_2).twice.and_call_original expect { instance.meth_1 :a }.to raise_error ArgumentError expect { instance.meth_2 {} }.to raise_error ArgumentError expect { instance.meth_2(:a, :b) {} }.to raise_error ArgumentError end it 'warns when you override an existing implementation' do expect(RSpec).to receive(:warning).with(/overriding a previous stub implementation of `meth_1`.*#{__FILE__}:#{__LINE__ + 1}/) expect(instance).to receive(:meth_1) { true }.and_call_original instance.meth_1 end context "for singleton methods" do it 'works' do def instance.foo; :bar; end expect(instance).to receive(:foo).and_call_original expect(instance.foo).to eq(:bar) end it 'works for SimpleDelegator subclasses', :if => (RUBY_VERSION.to_f > 1.8) do inst = Class.new(SimpleDelegator).new(1) def inst.foo; :bar; end expect(inst).to receive(:foo).and_call_original expect(inst.foo).to eq(:bar) end end it 'works for methods added through an extended module' do instance.extend Module.new { def foo; :bar; end } expect(instance).to receive(:foo).and_call_original expect(instance.foo).to eq(:bar) end it "works for method added through an extended module onto a class's ancestor" do sub_sub_klass = Class.new(Class.new(klass)) klass.extend Module.new { def foo; :bar; end } expect(sub_sub_klass).to receive(:foo).and_call_original expect(sub_sub_klass.foo).to eq(:bar) end it "finds the method on the most direct ancestor even if the method " \ "is available on more distant ancestors" do klass.extend Module.new { def foo; :klass_bar; end } sub_klass = Class.new(klass) sub_klass.extend Module.new { def foo; :sub_klass_bar; end } expect(sub_klass).to receive(:foo).and_call_original expect(sub_klass.foo).to eq(:sub_klass_bar) end it "finds the method on the most direct singleton class ancestors even if the method " \ "is available on more distant ancestors" do klass.extend Module.new { def foo; :klass_bar; end } sub_klass = Class.new(klass) { def self.foo; :sub_klass_bar; end } sub_sub_klass = Class.new(sub_klass) expect(sub_sub_klass).to receive(:foo).and_call_original expect(sub_sub_klass.foo).to eq(:sub_klass_bar) end context 'when using any_instance' do it 'works for instance methods defined on the class' do expect_any_instance_of(klass).to receive(:meth_1).and_call_original expect(klass.new.meth_1).to eq(:original) end if RSpec::Support::RubyFeatures.kw_args_supported? binding.eval(<<-RUBY, __FILE__, __LINE__) it 'works for instance methods that use double splat' do expect_any_instance_of(klass).to receive(:meth_3).and_call_original expect(klass.new.meth_3(x: :kwarg)).to eq({x: :kwarg}) end it 'works for instance methods that use optional keyword arguments' do expect_any_instance_of(klass).to receive(:meth_4).and_call_original expect(klass.new.meth_4).to eq(1) end it 'works for instance methods that use optional keyword arguments with an argument supplied' do expect_any_instance_of(klass).to receive(:meth_4).and_call_original expect(klass.new.meth_4(x: :kwarg)).to eq(:kwarg) end RUBY end if RSpec::Support::RubyFeatures.required_kw_args_supported? binding.eval(<<-RUBY, __FILE__, __LINE__) it 'works for instance methods that use required keyword arguments' do expect_any_instance_of(klass).to receive(:meth_5).and_call_original expect(klass.new.meth_5(x: :kwarg)).to eq(:kwarg) end RUBY end it 'works for instance methods defined on the superclass of the class' do subclass = Class.new(klass) expect_any_instance_of(subclass).to receive(:meth_1).and_call_original expect(subclass.new.meth_1).to eq(:original) end it 'works when mocking the method on one class and calling the method on an instance of a subclass' do expect_any_instance_of(klass).to receive(:meth_1).and_call_original expect(Class.new(klass).new.meth_1).to eq(:original) end end it 'works for class methods defined on a superclass' do subclass = Class.new(klass) expect(subclass).to receive(:new_instance).and_call_original expect(subclass.new_instance).to be_a(subclass) end context 'when a class method is stubbed in the superclass' do it 'still works for class methods defined on a superclass' do superclass = Class.new { def self.foo; "foo"; end } subclass = Class.new(superclass) allow(superclass).to receive(:foo).and_return(:fake) expect(subclass).to receive(:foo).and_call_original expect(superclass.foo).to be :fake expect(subclass.foo).to eq "foo" end end it 'works for class methods defined on a grandparent class' do sub_subclass = Class.new(Class.new(klass)) expect(sub_subclass).to receive(:new_instance).and_call_original expect(sub_subclass.new_instance).to be_a(sub_subclass) end it 'works for class methods defined on the Class class' do expect(klass).to receive(:new).and_call_original expect(klass.new).to be_an_instance_of(klass) end it "works for instance methods defined on the object's class's superclass" do subclass = Class.new(klass) inst = subclass.new expect(inst).to receive(:meth_1).and_call_original expect(inst.meth_1).to eq(:original) end it 'works for aliased methods' do klazz = Class.new do class << self alias alternate_new new end end expect(klazz).to receive(:alternate_new).and_call_original expect(klazz.alternate_new).to be_an_instance_of(klazz) end if RSpec::Support::RubyFeatures.kw_args_supported? binding.eval(<<-CODE, __FILE__, __LINE__) it "works for methods that accept keyword arguments" do def instance.foo(bar: nil); bar; end expect(instance).to receive(:foo).and_call_original expect(instance.foo(bar: "baz")).to eq("baz") end CODE end if RSpec::Support::RubyFeatures.required_kw_args_supported? binding.eval(<<-RUBY, __FILE__, __LINE__) context 'on an object with a method propagated by method_missing' do before do klass.class_exec do private def call_method_with_kwarg(arg, kwarg:) [arg, kwarg] end def method_missing(name, *args, **kwargs) if name.to_s == "method_with_kwarg" call_method_with_kwarg(*args, **kwargs) else super end end end end it 'works for the method propagated by method missing' do expect(instance).to receive(:method_with_kwarg).with(:arg, kwarg: 1).and_call_original expect(instance.method_with_kwarg(:arg, kwarg: 1)).to eq([:arg, 1]) end it 'works for the method of any_instance mock propagated by method missing' do expect_any_instance_of(klass).to receive(:method_with_kwarg).with(:arg, kwarg: 1).and_call_original expect(instance.method_with_kwarg(:arg, kwarg: 1)).to eq([:arg, 1]) end end RUBY end context 'on an object that defines method_missing' do before do klass.class_exec do private def method_missing(name, *args) if name.to_s == "greet_jack" "Hello, jack" else super end end end end it 'works when the method_missing definition handles the message' do expect(instance).to receive(:greet_jack).and_call_original expect(instance.greet_jack).to eq("Hello, jack") end it 'works for an any_instance partial mock' do expect_any_instance_of(klass).to receive(:greet_jack).and_call_original expect(instance.greet_jack).to eq("Hello, jack") end it 'raises an error for an unhandled message for an any_instance partial mock' do expect_any_instance_of(klass).to receive(:not_a_handled_message).and_call_original expect { instance.not_a_handled_message }.to raise_error(NameError, /not_a_handled_message/) end it 'raises an error on invocation if method_missing does not handle the message' do expect(instance).to receive(:not_a_handled_message).and_call_original # Note: it should raise a NoMethodError (and usually does), but # due to a weird rspec-expectations issue (see #183) it sometimes # raises a `NameError` when a `be_xxx` predicate matcher has been # recently used. `NameError` is the superclass of `NoMethodError` # so this example will pass regardless. # If/when we solve the rspec-expectations issue, this can (and should) # be changed to `NoMethodError`. expect { instance.not_a_handled_message }.to raise_error(NameError, /not_a_handled_message/) end end end context "on a partial double that overrides #method" do let(:request_klass) do Struct.new(:method, :url) do def perform :the_response end def self.method :some_method end end end let(:request) { request_klass.new(:get, "http://foo.com/bar") } it 'still works even though #method has been overridden' do expect(request).to receive(:perform).and_call_original expect(request.perform).to eq(:the_response) end it 'works for a singleton method' do def request.perform :a_response end expect(request).to receive(:perform).and_call_original expect(request.perform).to eq(:a_response) end end context "on a pure test double" do let(:instance) { double } it 'raises an error even if the double object responds to the message' do expect(instance.to_s).to be_a(String) mock_expectation = expect(instance).to receive(:to_s) instance.to_s # to satisfy the expectation expect { mock_expectation.and_call_original }.to raise_error(/pure test double.*and_call_original.*partial double/i) end end end rspec-mocks-3.13.0/spec/rspec/mocks/and_invoke_spec.rb000066400000000000000000000027171455767030500227050ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe 'and_invoke' do let(:obj) { double('obj') } context 'when a block is passed' do it 'raises ArgumentError' do expect { allow(obj).to receive(:foo).and_invoke('bar') { 'baz' } }.to raise_error(ArgumentError, /implementation block/i) end end context 'when no argument is passed' do it 'raises ArgumentError' do expect { allow(obj).to receive(:foo).and_invoke }.to raise_error(ArgumentError) end end context 'when a non-callable are passed in any position' do let(:non_callable) { nil } let(:callable) { lambda { nil } } it 'raises ArgumentError' do error = [ArgumentError, "Arguments to `and_invoke` must be callable."] expect { allow(obj).to receive(:foo).and_invoke(non_callable) }.to raise_error(*error) expect { allow(obj).to receive(:foo).and_invoke(callable, non_callable) }.to raise_error(*error) end end context 'when calling passed callables' do let(:dbl) { double } it 'passes the arguments into the callable' do expect(dbl).to receive(:square_then_cube).and_invoke(lambda { |i| i ** 2 }, lambda { |i| i ** 3 }) expect(dbl.square_then_cube(2)).to eq 4 expect(dbl.square_then_cube(2)).to eq 8 end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/and_return_spec.rb000066400000000000000000000010671455767030500227260ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe 'and_return' do let(:obj) { double('obj') } context 'when a block is passed' do it 'raises ArgumentError' do expect { allow(obj).to receive(:foo).and_return('bar') { 'baz' } }.to raise_error(ArgumentError, /implementation block/i) end end context 'when no argument is passed' do it 'raises ArgumentError' do expect { allow(obj).to receive(:foo).and_return }.to raise_error(ArgumentError) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/and_wrap_original_spec.rb000066400000000000000000000067221455767030500242470ustar00rootroot00000000000000RSpec.describe "and_wrap_original" do context "on a partial double" do let(:klass) do Class.new do def results (1..100).to_a end end end let(:instance) { klass.new } shared_examples "using and_wrap_original" do it "allows us to modify the results of the original method" do expect { allow_it.to receive(:results).and_wrap_original do |m| m.call.first(10) end }.to change { instance.results.size }.from(100).to(10) end it "raises a name error if the method does not exist" do expect { allow_it.to receive(:no_results).and_wrap_original { |m| m.call } instance.no_results }.to raise_error NameError end it "passes along the original method" do passed_method = nil original_method = instance.method(:results) allow_it.to receive(:results).and_wrap_original { |m| passed_method = m } instance.results expect(passed_method.call).to eq(original_method.call) end it "passes along the message arguments" do values = nil allow_it.to receive(:results).and_wrap_original { |_, *args| values = args } instance.results(1, 2, 3) expect(values).to eq [1, 2, 3] end it "passes along any supplied block" do value = nil allow_it.to receive(:results).and_wrap_original { |&b| value = b } instance.results(&(block = proc {})) expect(value).to eq block end it "ignores previous stubs" do allow_it.to receive(:results) { "results" } allow_it.to receive(:results).and_wrap_original { |m| m.call } expect(instance.results).to_not eq "results" end it "can be constrained by specific arguments" do allow_it.to receive(:results) { :all } allow_it.to receive(:results).with(5).and_wrap_original { |m, n| m.call.first(n) } expect(instance.results 5).to eq [1, 2, 3, 4, 5] expect(instance.results).to eq :all end if RSpec::Support::RubyFeatures.kw_args_supported? binding.eval(<<-CODE, __FILE__, __LINE__) it "works for methods that accept keyword arguments, using a keyword argument block" do def instance.foo(bar: nil); bar; end allow(instance).to receive(:foo).and_wrap_original { |m, **kwargs| m.call(**kwargs) } expect(instance.foo(bar: "baz")).to eq("baz") end CODE end end describe "allow(...).to receive(...).and_wrap_original" do it_behaves_like "using and_wrap_original" do let(:allow_it) { allow(instance) } end end describe "allow_any_instance_of(...).to receive(...).and_wrap_original" do it_behaves_like "using and_wrap_original" do let(:allow_it) { allow_any_instance_of(klass) } end end end context "on a pure test double" do let(:instance) { double :my_method => :my_value } it 'raises an error' do mock_expectation = allow(instance).to receive(:my_method) expect { mock_expectation.and_wrap_original }.to raise_error(/pure test double.*and_wrap_original.*partial double/i) end it 'raises an error even if the double object responds to the message' do mock_expectation = allow(instance).to receive(:inspect) expect { mock_expectation.and_wrap_original }.to raise_error(/pure test double.*and_wrap_original.*partial double/i) end end end rspec-mocks-3.13.0/spec/rspec/mocks/and_yield_spec.rb000066400000000000000000000124111455767030500225100ustar00rootroot00000000000000RSpec.describe RSpec::Mocks::Double do let(:obj) { double } describe "#and_yield" do context 'when the method double has been constrained by `with`' do it 'uses the default stub if the provided args do not match' do allow(obj).to receive(:foo) { 15 } allow(obj).to receive(:foo).with(:yield).and_yield # should_receive is required to trigger the bug: # https://github.com/rspec/rspec-mocks/issues/127 expect(obj).to receive(:foo) expect(obj.foo(:dont_yield)).to eq(15) end end context "with eval context as block argument" do it "evaluates the supplied block as it is read" do evaluated = false allow(obj).to receive(:method_that_accepts_a_block).and_yield do |_eval_context| evaluated = true end expect(evaluated).to be_truthy end it "passes an eval context object to the supplied block" do allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| expect(eval_context).not_to be_nil end end it "evaluates the block passed to the stubbed method in the context of the supplied eval context" do expected_eval_context = nil actual_eval_context = nil allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| expected_eval_context = eval_context end obj.method_that_accepts_a_block do actual_eval_context = self end expect(actual_eval_context).to equal(expected_eval_context) end context "and no yielded arguments" do it "passes when expectations set on the eval context are met" do configured_eval_context = nil allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| configured_eval_context = eval_context expect(configured_eval_context).to receive(:foo) end obj.method_that_accepts_a_block do foo end verify configured_eval_context end it "fails when expectations set on the eval context are not met" do configured_eval_context = nil allow(obj).to receive(:method_that_accepts_a_block).and_yield do |eval_context| configured_eval_context = eval_context expect(configured_eval_context).to receive(:foo) end obj.method_that_accepts_a_block do # foo is not called here end expect { verify configured_eval_context }.to fail end end context "and yielded arguments" do it "passes when expectations set on the eval context and yielded arguments are met" do configured_eval_context = nil yielded_arg = Object.new allow(obj).to receive(:method_that_accepts_a_block).and_yield(yielded_arg) do |eval_context| configured_eval_context = eval_context expect(configured_eval_context).to receive(:foo) expect(yielded_arg).to receive(:bar) end obj.method_that_accepts_a_block do |object| object.bar foo end verify configured_eval_context verify yielded_arg end context "that are optional", :if => RSpec::Support::RubyFeatures.optional_and_splat_args_supported? do it "yields the default argument when the argument is not given" do pending "Not sure how to achieve this yet. See rspec/rspec-mocks#714 and rspec/rspec-support#85." default_arg = Object.new object = Object.new allow(object).to receive(:a_message).and_yield expect(default_arg).to receive(:bar) eval("object.a_message { |receiver=default_arg| receiver.bar }") end it "yields given argument when the argument is given" do default_arg = Object.new allow(default_arg).to receive(:bar) given_arg = Object.new object = Object.new allow(object).to receive(:a_message).and_yield(given_arg) expect(given_arg).to receive(:bar) eval("object.a_message { |receiver=default_arg| receiver.bar }") end end it 'can yield to a block that uses `super`' do klass = Class.new { def foo; 13; end } subklass = Class.new(klass) { def foo(arg); arg.bar { super() }; end } arg = double expect(arg).to receive(:bar).and_yield instance = subklass.new instance.foo(arg) end it "fails when expectations set on the eval context and yielded arguments are not met" do configured_eval_context = nil yielded_arg = Object.new allow(obj).to receive(:method_that_accepts_a_block).and_yield(yielded_arg) do |eval_context| configured_eval_context = eval_context expect(configured_eval_context).to receive(:foo) expect(yielded_arg).to receive(:bar) end obj.method_that_accepts_a_block do |_obj| # _obj.bar is not called here # foo is not called here end expect { verify configured_eval_context }.to fail expect { verify yielded_arg }.to fail end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/any_instance/000077500000000000000000000000001455767030500216755ustar00rootroot00000000000000rspec-mocks-3.13.0/spec/rspec/mocks/any_instance/message_chains_spec.rb000066400000000000000000000027771455767030500262220ustar00rootroot00000000000000RSpec.describe RSpec::Mocks::AnyInstance::MessageChains do let(:recorder) { double } let(:chains) { RSpec::Mocks::AnyInstance::MessageChains.new } let(:stub_chain) { RSpec::Mocks::AnyInstance::StubChain.new recorder } let(:expectation_chain) { RSpec::Mocks::AnyInstance::PositiveExpectationChain.new recorder } it "knows if a method does not have an expectation set on it" do chains.add(:method_name, stub_chain) expect(chains.has_expectation?(:method_name)).to be_falsey end it "knows if a method has an expectation set on it" do chains.add(:method_name, stub_chain) chains.add(:method_name, expectation_chain) expect(chains.has_expectation?(:method_name)).to be_truthy end it "can remove all stub chains" do chains.add(:method_name, stub_chain) chains.add(:method_name, expectation_chain) chains.add(:method_name, RSpec::Mocks::AnyInstance::StubChain.new(recorder)) chains.remove_stub_chains_for!(:method_name) expect(chains[:method_name]).to eq([expectation_chain]) end context "creating stub chains" do it "understands how to add a stub chain for a method" do chains.add(:method_name, stub_chain) expect(chains[:method_name]).to eq([stub_chain]) end it "allows multiple stub chains for a method" do chains.add(:method_name, stub_chain) chains.add(:method_name, another_stub_chain = RSpec::Mocks::AnyInstance::StubChain.new(recorder)) expect(chains[:method_name]).to eq([stub_chain, another_stub_chain]) end end end rspec-mocks-3.13.0/spec/rspec/mocks/any_instance_spec.rb000066400000000000000000001500501455767030500232350ustar00rootroot00000000000000require 'delegate' module AnyInstanceSpec class GrandparentClass def foo(_a) 'bar' end def grandparent_method 1 end end class ParentClass < GrandparentClass def foo super(:a) end def caller_of_parent_aliased_method parent_aliased_method end alias parent_aliased_method grandparent_method end class ChildClass < ParentClass end end module RSpec module Mocks RSpec.describe "#any_instance" do class CustomErrorForAnyInstanceSpec < StandardError; end let(:klass) do Class.new do def existing_method; :existing_method_return_value; end def existing_method_with_arguments(_a, _b=nil); :existing_method_with_arguments_return_value; end def another_existing_method; end private def private_method; :private_method_return_value; end end end let(:existing_method_return_value) { :existing_method_return_value } context "chain" do it "yields the value specified" do allow_any_instance_of(klass).to receive(:foo).and_yield(1).and_yield(2) expect { |b| klass.new.foo(&b) }.to yield_successive_args(1, 2) end end context "invocation order" do context "when stubbing" do it "raises an error if 'with' follows 'and_return'" do expect { allow_any_instance_of(klass).to receive(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_raise'" do expect { allow_any_instance_of(klass).to receive(:foo).and_raise(1).with("1") }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_yield'" do expect { allow_any_instance_of(klass).to receive(:foo).and_yield(1).with("1") }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_throw'" do expect { allow_any_instance_of(klass).to receive(:foo).and_throw(:ball).with("football") }.to raise_error(NoMethodError) end it "allows chaining 'and_yield'" do allow_any_instance_of(klass).to receive(:foo).and_yield(1).and_yield(2).and_yield(3) expect { |b| klass.new.foo(&b) }.to yield_successive_args(1, 2, 3) end end context "when setting a message expectation" do it "raises an error if 'with' follows 'and_return'" do pending "see Github issue #42" expect { expect_any_instance_of(klass).to receive(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_raise'" do pending "see Github issue #42" expect { expect_any_instance_of(klass).to receive(:foo).and_raise(1).with("1") }.to raise_error(NoMethodError) end end end context "when stubbing" do it "does not suppress an exception when a method that doesn't exist is invoked" do allow_any_instance_of(klass).to receive(:foo) expect { klass.new.bar }.to raise_error(NoMethodError) end context 'multiple methods' do it "allows multiple methods to be stubbed in a single invocation" do allow_any_instance_of(klass).to receive_messages(:foo => 'foo', :bar => 'bar') instance = klass.new expect(instance.foo).to eq('foo') expect(instance.bar).to eq('bar') end context "allows a chain of methods to be stubbed using #receive_message_chain" do example "given symbols representing the methods" do allow_any_instance_of(klass).to receive_message_chain(:one, :two, :three).and_return(:four) expect(klass.new.one.two.three).to eq(:four) end example "given a hash as the last argument uses the value as the expected return value" do allow_any_instance_of(klass).to receive_message_chain(:one, :two, :three => :four) expect(klass.new.one.two.three).to eq(:four) end example "given a string of '.' separated method names representing the chain" do allow_any_instance_of(klass).to receive_message_chain('one.two.three').and_return(:four) expect(klass.new.one.two.three).to eq(:four) end it "can constrain the return value by the argument to the last call" do allow_any_instance_of(klass).to receive_message_chain(:one, :plus).with(1) { 2 } allow_any_instance_of(klass).to receive_message_chain(:one, :plus).with(2) { 3 } expect(klass.new.one.plus(1)).to eq(2) expect(klass.new.one.plus(2)).to eq(3) end it 'can use a chain of methods to perform an expectation' do expect_any_instance_of(klass).to receive_message_chain('message1.message2').with('some args') klass.new.message1.message2('some args') end end end context "behaves as 'every instance'" do let(:super_class) { Class.new { def foo; 'bar'; end } } let(:sub_class) { Class.new(super_class) { def bar; 'foo'; end } } it "stubs every instance in the spec" do allow_any_instance_of(klass).to receive(:foo).and_return(result = Object.new) expect(klass.new.foo).to eq(result) expect(klass.new.foo).to eq(result) end it "stubs instance created before any_instance was called" do instance = klass.new allow_any_instance_of(klass).to receive(:foo).and_return(result = Object.new) expect(instance.foo).to eq(result) end it 'handles freeze and duplication correctly' do allow_any_instance_of(String).to receive(:any_method) foo = 'foo'.freeze expect(foo.dup.concat 'bar').to eq 'foobar' end it 'handles stubbing on super and subclasses' do allow_any_instance_of(super_class).to receive(:foo) allow_any_instance_of(sub_class).to receive(:foo).and_return('baz') expect(sub_class.new.foo).to eq('baz') end it 'handles stubbing on a sub class when a super class is stubbed differently' do expect_any_instance_of(super_class).to receive(:foo) expect_any_instance_of(sub_class).to receive(:bar).and_return('baz') expect(sub_class.new.bar).to eq('baz') expect { verify_all }.to fail_with "Exactly one instance should have received the following message(s) but didn't: foo" end it 'handles method restoration on subclasses' do allow_any_instance_of(super_class).to receive(:foo) allow_any_instance_of(sub_class).to receive(:foo) allow_any_instance_of(sub_class).to receive(:foo).and_call_original expect(sub_class.new.foo).to eq("bar") end end context "when the class has a prepended module", :if => Support::RubyFeatures.module_prepends_supported? do it 'allows stubbing a method that is not defined on the prepended module' do klass.class_eval { prepend Module.new { def other; end } } allow_any_instance_of(klass).to receive(:foo).and_return(45) expect(klass.new.foo).to eq(45) end it 'prevents stubbing a method that is defined on the prepended module' do klass.class_eval { prepend Module.new { def foo; end } } expect { allow_any_instance_of(klass).to receive(:foo).and_return(45) }.to fail_with(/prepended module/) end it 'allows stubbing a chain starting with a method that is not defined on the prepended module' do klass.class_eval { prepend Module.new { def other; end } } allow_any_instance_of(klass).to receive_message_chain(:foo, :bar).and_return(45) expect(klass.new.foo.bar).to eq(45) end it 'prevents stubbing a chain starting with a method that is defined on the prepended module' do klass.class_eval { prepend Module.new { def foo; end } } expect { allow_any_instance_of(klass).to receive_message_chain(:foo, :bar).and_return(45) }.to fail_with(/prepended module/) end end context 'aliased methods' do it 'tracks aliased method calls' do instance = AnyInstanceSpec::ParentClass.new expect_any_instance_of(AnyInstanceSpec::ParentClass).to receive(:parent_aliased_method).with(no_args).and_return(2) expect(instance.caller_of_parent_aliased_method).to eq 2 reset_all expect(instance.caller_of_parent_aliased_method).to eq 1 end end context "with argument matching" do before do allow_any_instance_of(klass).to receive(:foo).with(:param_one, :param_two).and_return(:result_one) allow_any_instance_of(klass).to receive(:foo).with(:param_three, :param_four).and_return(:result_two) end it "returns the stubbed value when arguments match" do instance = klass.new expect(instance.foo(:param_one, :param_two)).to eq(:result_one) expect(instance.foo(:param_three, :param_four)).to eq(:result_two) end it "fails the spec with an expectation error when the arguments do not match" do expect do klass.new.foo(:param_one, :param_three) end.to fail end end context "with multiple stubs" do before do allow_any_instance_of(klass).to receive(:foo).and_return(1) allow_any_instance_of(klass).to receive(:bar).and_return(2) end it "stubs a method" do instance = klass.new expect(instance.foo).to eq(1) expect(instance.bar).to eq(2) end it "returns the same value for calls on different instances" do expect(klass.new.foo).to eq(klass.new.foo) expect(klass.new.bar).to eq(klass.new.bar) end end context "with #and_return" do it "can stub a method that doesn't exist" do allow_any_instance_of(klass).to receive(:foo).and_return(1) expect(klass.new.foo).to eq(1) end it "can stub a method that exists" do allow_any_instance_of(klass).to receive(:existing_method).and_return(1) expect(klass.new.existing_method).to eq(1) end it "returns the same object for calls on different instances" do return_value = Object.new allow_any_instance_of(klass).to receive(:foo).and_return(return_value) expect(klass.new.foo).to be(return_value) expect(klass.new.foo).to be(return_value) end it "can change how instances responds in the middle of an example" do instance = klass.new allow_any_instance_of(klass).to receive(:foo).and_return(1) expect(instance.foo).to eq(1) allow_any_instance_of(klass).to receive(:foo).and_return(2) expect(instance.foo).to eq(2) allow_any_instance_of(klass).to receive(:foo).and_raise("boom") expect { instance.foo }.to raise_error("boom") end end context "with #and_yield" do it "yields the value specified" do yielded_value = Object.new allow_any_instance_of(klass).to receive(:foo).and_yield(yielded_value) expect { |b| klass.new.foo(&b) }.to yield_with_args(yielded_value) end end context 'with #and_call_original and competing #with' do let(:klass) { Struct.new(:a_method) } it 'can combine and_call_original, with, and_return' do allow_any_instance_of(klass).to receive(:a_method).and_call_original allow_any_instance_of(klass).to receive(:a_method).with(:arg).and_return('value') expect(klass.new('org').a_method).to eq 'org' expect(klass.new.a_method(:arg)).to eq 'value' end end context "with #and_raise" do it "can stub a method that doesn't exist" do allow_any_instance_of(klass).to receive(:foo).and_raise(CustomErrorForAnyInstanceSpec) expect { klass.new.foo }.to raise_error(CustomErrorForAnyInstanceSpec) end it "can stub a method that exists" do allow_any_instance_of(klass).to receive(:existing_method).and_raise(CustomErrorForAnyInstanceSpec) expect { klass.new.existing_method }.to raise_error(CustomErrorForAnyInstanceSpec) end end context "with #and_throw" do it "can stub a method that doesn't exist" do allow_any_instance_of(klass).to receive(:foo).and_throw(:up) expect { klass.new.foo }.to throw_symbol(:up) end it "can stub a method that exists" do allow_any_instance_of(klass).to receive(:existing_method).and_throw(:up) expect { klass.new.existing_method }.to throw_symbol(:up) end end context "with a block" do it "stubs a method" do allow_any_instance_of(klass).to receive(:foo) { 1 } expect(klass.new.foo).to eq(1) end it "returns the same computed value for calls on different instances" do allow_any_instance_of(klass).to receive(:foo) { 1 + 2 } expect(klass.new.foo).to eq(klass.new.foo) end end context "when partially mocking objects" do let(:obj) { klass.new } it "resets partially mocked objects correctly" do allow_any_instance_of(klass).to receive(:existing_method).and_return("stubbed value") # Simply resetting the proxy doesn't work # what we need to have happen is # ::RSpec::Mocks.space.any_instance_recorder_for(klass).stop_all_observation! # but that is never invoked in :: expect { verify_all }.to( change { obj.existing_method }.from("stubbed value").to(:existing_method_return_value) ) end end context "core ruby objects" do it "works uniformly across *everything*" do allow_any_instance_of(Object).to receive(:foo).and_return(1) expect(Object.new.foo).to eq(1) end it "works with the non-standard constructor []" do allow_any_instance_of(Array).to receive(:foo).and_return(1) expect([].foo).to eq(1) end it "works with the non-standard constructor {}" do allow_any_instance_of(Hash).to receive(:foo).and_return(1) expect({}.foo).to eq(1) end it "works with the non-standard constructor \"\"" do allow_any_instance_of(String).to receive(:foo).and_return(1) expect("".dup.foo).to eq(1) end it "works with the non-standard constructor \'\'" do allow_any_instance_of(String).to receive(:foo).and_return(1) expect(''.dup.foo).to eq(1) end it "works with the non-standard constructor module" do allow_any_instance_of(Module).to receive(:foo).and_return(1) module RSpec::SampleRspecTestModule; end expect(RSpec::SampleRspecTestModule.foo).to eq(1) end it "works with the non-standard constructor class" do allow_any_instance_of(Class).to receive(:foo).and_return(1) class RSpec::SampleRspecTestClass; end expect(RSpec::SampleRspecTestClass.foo).to eq(1) end end end context "unstubbing using `and_call_original`" do it "replaces the stubbed method with the original method" do allow_any_instance_of(klass).to receive(:existing_method) allow_any_instance_of(klass).to receive(:existing_method).and_call_original expect(klass.new.existing_method).to eq(:existing_method_return_value) end it "removes all stubs with the supplied method name" do allow_any_instance_of(klass).to receive(:existing_method).with(1) allow_any_instance_of(klass).to receive(:existing_method).with(2) allow_any_instance_of(klass).to receive(:existing_method).and_call_original expect(klass.new.existing_method).to eq(:existing_method_return_value) end it "removes stubs even if they have already been invoked" do allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = klass.new obj.existing_method allow_any_instance_of(klass).to receive(:existing_method).and_call_original expect(obj.existing_method).to eq(:existing_method_return_value) end it "removes stubs from sub class after Invocation when super class was originally stubbed" do allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = Class.new(klass).new expect(obj.existing_method).to eq(:any_instance_value) allow_any_instance_of(klass).to receive(:existing_method).and_call_original expect(obj.existing_method).to eq(:existing_method_return_value) end it "removes any stubs set directly on an instance" do allow_any_instance_of(klass).to receive(:existing_method).and_return(:any_instance_value) obj = klass.new allow(obj).to receive(:existing_method).and_return(:local_method) allow_any_instance_of(klass).to receive(:existing_method).and_call_original expect(obj.existing_method).to eq(:existing_method_return_value) end it "does not remove any expectations with the same method name" do expect_any_instance_of(klass).to receive(:existing_method_with_arguments).with(3).and_return(:three) allow_any_instance_of(klass).to receive(:existing_method_with_arguments).with(1) allow_any_instance_of(klass).to receive(:existing_method_with_arguments).with(2) allow_any_instance_of(klass).to receive(:existing_method_with_arguments).and_call_original expect(klass.new.existing_method_with_arguments(3)).to eq(:three) end it 'does not get confused about string vs symbol usage for the message' do allow_any_instance_of(klass).to receive(:existing_method) { :stubbed } allow_any_instance_of(klass).to receive("existing_method").and_call_original expect(klass.new.existing_method).to eq(:existing_method_return_value) end end context "expect_any_instance_of(...).not_to receive" do it "fails if the method is called" do expect_any_instance_of(klass).not_to receive(:existing_method) expect_fast_failure_from(klass.new) do |instance| instance.existing_method end end it "passes if no method is called" do expect { expect_any_instance_of(klass).not_to receive(:existing_method) }.to_not raise_error end it "passes if only a different method is called" do expect_any_instance_of(klass).not_to receive(:existing_method) expect { klass.new.another_existing_method }.to_not raise_error end it "affects previously stubbed instances" do instance = klass.new allow_any_instance_of(klass).to receive(:foo).and_return(1) expect(instance.foo).to eq(1) expect_any_instance_of(klass).not_to receive(:foo) expect_fast_failure_from(instance) do instance.foo end end context "with constraints" do it "fails if the method is called with the specified parameters" do expect_any_instance_of(klass).not_to receive(:existing_method_with_arguments).with(:argument_one, :argument_two) expect_fast_failure_from(klass.new) do |instance| instance.existing_method_with_arguments(:argument_one, :argument_two) end end it "passes if the method is called with different parameters" do expect_any_instance_of(klass).not_to receive(:existing_method_with_arguments).with(:argument_one, :argument_two) expect { klass.new.existing_method_with_arguments(:argument_three, :argument_four) }.to_not raise_error end end context 'when used in combination with should_receive' do it 'passes if only the expected message is received' do expect_any_instance_of(klass).to receive(:foo) expect_any_instance_of(klass).not_to receive(:bar) klass.new.foo verify_all end end it "prevents confusing double-negative expressions involving `never`" do expect { expect_any_instance_of(klass).not_to receive(:not_expected).never }.to raise_error(/trying to negate it again/) end end context "setting a message expectation" do let(:foo_expectation_error_message) { 'Exactly one instance should have received the following message(s) but didn\'t: foo' } let(:existing_method_expectation_error_message) { 'Exactly one instance should have received the following message(s) but didn\'t: existing_method' } it "handles inspect accessing expected methods" do klass.class_eval do def inspect "The contents of output: #{stdout}" end end expect_any_instance_of(klass).to receive(:stdout).at_least(:twice) expect do klass.new.stdout klass.new.stdout end.to raise_error(/The message 'stdout' was received by/) reset_all end it "affects previously stubbed instances" do instance = klass.new allow_any_instance_of(klass).to receive(:foo).and_return(1) expect(instance.foo).to eq(1) expect_any_instance_of(klass).to receive(:foo).with(2).and_return(2) expect(instance.foo(2)).to eq(2) end it "does not set the expectation on every instance" do # Setup an unrelated object of the same class that won't receive the expected message. allow('non-related object'.dup).to receive(:non_related_method) expect_any_instance_of(Object).to receive(:foo) 'something'.dup.foo end it "does not modify the return value of stubs set on an instance" do expect_any_instance_of(Object).to receive(:foo).twice object = Object.new allow(object).to receive(:foo).and_return(3) expect(object.foo).to eq(3) expect(object.foo).to eq(3) end it "does not modify the return value of stubs set on an instance of a subclass" do subklass = Class.new(klass) subinstance = subklass.new allow_any_instance_of(klass).to receive(:foo).and_return(1) expect(subinstance.foo).to eq(1) expect_any_instance_of(klass).to receive(:foo).with(2).and_return(2) expect(subinstance.foo(2)).to eq(2) end it "properly notifies any instance recorders at multiple levels of hierarchy when a directly stubbed object receives a message" do subclass = Class.new(klass) instance = subclass.new expect_any_instance_of(klass).to receive(:msg_1) expect_any_instance_of(subclass).to receive(:msg_2) allow(instance).to receive_messages(:msg_1 => "a", :msg_2 => "b") expect(instance.msg_1).to eq("a") expect(instance.msg_2).to eq("b") end it "properly notifies any instance recorders when they are created after the object's mock proxy" do object = Object.new allow(object).to receive(:bar) expect_any_instance_of(Object).to receive(:foo).twice allow(object).to receive(:foo).and_return(3) expect(object.foo).to eq(3) expect(object.foo).to eq(3) end context "when the class has a prepended module", :if => Support::RubyFeatures.module_prepends_supported? do it 'allows mocking a method that is not defined on the prepended module' do klass.class_eval { prepend Module.new { def other; end } } expect_any_instance_of(klass).to receive(:foo).and_return(45) expect(klass.new.foo).to eq(45) end it 'prevents mocking a method that is defined on the prepended module' do klass.class_eval { prepend Module.new { def foo; end } } expect { expect_any_instance_of(klass).to receive(:foo).and_return(45) }.to fail_with(/prepended module/) end end context "when the class has an included module" do it 'allows mocking a method that is defined on the module' do mod = Module.new { def foo; end } klass.class_eval { include mod } expect_any_instance_of(mod).to receive(:foo).and_return(45) expect(klass.new.foo).to eq(45) end end context "when an instance has been directly stubbed" do it "fails when a second instance to receive the message" do expect_any_instance_of(klass).to receive(:foo) instance_1 = klass.new allow(instance_1).to receive(:foo).and_return(17) expect(instance_1.foo).to eq(17) expect { klass.new.foo }.to fail_with(/has already been received/) end end context "when argument matching is used and an instance has stubbed the message" do it "fails on verify if the arguments do not match" do expect_any_instance_of(klass).to receive(:foo).with(3) instance = klass.new allow(instance).to receive(:foo).and_return(2) expect(instance.foo(4)).to eq(2) expect { verify_all }.to fail end it "passes on verify if the arguments do match" do expect_any_instance_of(klass).to receive(:foo).with(3) instance = klass.new allow(instance).to receive(:foo).and_return(2) expect(instance.foo(3)).to eq(2) expect { verify_all }.not_to raise_error end end context "with an expectation is set on a method which does not exist" do it "returns the expected value" do expect_any_instance_of(klass).to receive(:foo).and_return(1) expect(klass.new.foo(1)).to eq(1) end it "fails if an instance is created but no invocation occurs" do expect do expect_any_instance_of(klass).to receive(:foo) klass.new verify_all end.to fail_with foo_expectation_error_message end it "fails if no instance is created" do expect do expect_any_instance_of(klass).to receive(:foo).and_return(1) verify_all end.to fail_with foo_expectation_error_message end it "fails if no instance is created and there are multiple expectations" do expect do expect_any_instance_of(klass).to receive(:foo) expect_any_instance_of(klass).to receive(:bar) verify_all end.to fail_with 'Exactly one instance should have received the following message(s) but didn\'t: bar, foo' end it "allows expectations on instances to take priority" do expect_any_instance_of(klass).to receive(:foo) klass.new.foo instance = klass.new expect(instance).to receive(:foo).and_return(result = Object.new) expect(instance.foo).to eq(result) end context "behaves as 'exactly one instance'" do it "passes if subsequent invocations do not receive that message" do expect_any_instance_of(klass).to receive(:foo) klass.new.foo klass.new end it "fails if the method is invoked on a second instance" do instance_one = klass.new instance_two = klass.new expect do expect_any_instance_of(klass).to receive(:foo) instance_one.foo instance_two.foo end.to fail_with(/The message 'foo' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) end end context "normal expectations on the class object" do it "fail when unfulfilled" do expect do expect_any_instance_of(klass).to receive(:foo) expect(klass).to receive(:woot) klass.new.foo verify_all end.to(fail do |error| expect(error.message).not_to eq(existing_method_expectation_error_message) end) end it "pass when expectations are met" do expect_any_instance_of(klass).to receive(:foo) expect(klass).to receive(:woot).and_return(result = Object.new) klass.new.foo expect(klass.woot).to eq(result) end end end context "with an expectation is set on a method that exists" do it "returns the expected value" do expect_any_instance_of(klass).to receive(:existing_method).and_return(1) expect(klass.new.existing_method(1)).to eq(1) end it "fails if an instance is created but no invocation occurs" do expect do expect_any_instance_of(klass).to receive(:existing_method) klass.new verify_all end.to fail_with existing_method_expectation_error_message end it "fails if no instance is created" do expect do expect_any_instance_of(klass).to receive(:existing_method) verify_all end.to fail_with existing_method_expectation_error_message end it "fails if no instance is created and there are multiple expectations" do expect do expect_any_instance_of(klass).to receive(:existing_method) expect_any_instance_of(klass).to receive(:another_existing_method) verify_all end.to fail_with 'Exactly one instance should have received the following message(s) but didn\'t: another_existing_method, existing_method' end context "after any one instance has received a message" do it "passes if subsequent invocations do not receive that message" do expect_any_instance_of(klass).to receive(:existing_method) klass.new.existing_method klass.new end it "fails if the method is invoked on a second instance" do instance_one = klass.new instance_two = klass.new expect do expect_any_instance_of(klass).to receive(:existing_method) instance_one.existing_method instance_two.existing_method end.to fail_with(/The message 'existing_method' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) end end end it 'works with a BasicObject subclass that mixes in Kernel', :if => defined?(BasicObject) do klazz = Class.new(BasicObject) do include ::Kernel def foo; end end expect_any_instance_of(klazz).to receive(:foo) klazz.new.foo end it 'works with a SimpleDelegator subclass', :if => (RUBY_VERSION.to_f > 1.8) do klazz = Class.new(SimpleDelegator) do def foo; end end expect_any_instance_of(klazz).to receive(:foo) klazz.new(Object.new).foo end context "with argument matching" do before do expect_any_instance_of(klass).to receive(:foo).with(:param_one, :param_two).and_return(:result_one) expect_any_instance_of(klass).to receive(:foo).with(:param_three, :param_four).and_return(:result_two) end it "returns the expected value when arguments match" do instance = klass.new expect(instance.foo(:param_one, :param_two)).to eq(:result_one) expect(instance.foo(:param_three, :param_four)).to eq(:result_two) end it "fails when the arguments match but different instances are used" do instances = Array.new(2) { klass.new } expect do expect(instances[0].foo(:param_one, :param_two)).to eq(:result_one) expect(instances[1].foo(:param_three, :param_four)).to eq(:result_two) end.to fail # ignore the fact that should_receive expectations were not met instances.each { |instance| reset instance } end it "is not affected by the invocation of existing methods on other instances" do expect(klass.new.existing_method_with_arguments(:param_one, :param_two)).to eq(:existing_method_with_arguments_return_value) instance = klass.new expect(instance.foo(:param_one, :param_two)).to eq(:result_one) expect(instance.foo(:param_three, :param_four)).to eq(:result_two) end it "fails when arguments do not match" do instance = klass.new expect do instance.foo(:param_one, :param_three) end.to fail # ignore the fact that should_receive expectations were not met reset instance end end context "message count" do context "the 'once' constraint" do it "passes for one invocation" do expect_any_instance_of(klass).to receive(:foo).once klass.new.foo end it "fails when no instances are declared" do expect do expect_any_instance_of(klass).to receive(:foo).once verify_all end.to fail_with foo_expectation_error_message end it "fails when an instance is declared but there are no invocations" do expect do expect_any_instance_of(klass).to receive(:foo).once klass.new verify_all end.to fail_with foo_expectation_error_message end it "fails for more than one invocation" do expect_any_instance_of(klass).to receive(:foo).once expect_fast_failure_from(klass.new) do |instance| 2.times { instance.foo } verify instance end end end context "the 'twice' constraint" do it "passes for two invocations" do expect_any_instance_of(klass).to receive(:foo).twice instance = klass.new 2.times { instance.foo } end it "fails for more than two invocations" do expect_any_instance_of(klass).to receive(:foo).twice expect_fast_failure_from(klass.new) do |instance| 3.times { instance.foo } verify instance end end end context "the 'thrice' constraint" do it "passes for three invocations" do expect_any_instance_of(klass).to receive(:foo).thrice instance = klass.new 3.times { instance.foo } end it "fails for more than three invocations" do expect_any_instance_of(klass).to receive(:foo).thrice expect_fast_failure_from(klass.new) do |instance| 4.times { instance.foo } verify instance end end it "fails for less than three invocations" do expect do expect_any_instance_of(klass).to receive(:foo).thrice instance = klass.new 2.times { instance.foo } verify instance end.to fail end end context "the 'exactly(n)' constraint" do describe "time alias" do it "passes for 1 invocation" do expect_any_instance_of(klass).to receive(:foo).exactly(1).time instance = klass.new instance.foo end it "fails for 2 invocations" do expect_any_instance_of(klass).to receive(:foo).exactly(1).time expect_fast_failure_from(klass.new) do |instance| 2.times { instance.foo } verify instance end end end it "passes for n invocations where n = 3" do expect_any_instance_of(klass).to receive(:foo).exactly(3).times instance = klass.new 3.times { instance.foo } end it "fails for n invocations where n < 3" do expect do expect_any_instance_of(klass).to receive(:foo).exactly(3).times instance = klass.new 2.times { instance.foo } verify instance end.to fail end it "fails for n invocations where n > 3" do expect_any_instance_of(klass).to receive(:foo).exactly(3).times expect_fast_failure_from(klass.new) do |instance| 4.times { instance.foo } verify instance end end end context "the 'at_least(n)' constraint" do it "passes for n invocations where n = 3" do expect_any_instance_of(klass).to receive(:foo).at_least(3).times instance = klass.new 3.times { instance.foo } end it "fails for n invocations where n < 3" do expect do expect_any_instance_of(klass).to receive(:foo).at_least(3).times instance = klass.new 2.times { instance.foo } verify instance end.to fail end it "passes for n invocations where n > 3" do expect_any_instance_of(klass).to receive(:foo).at_least(3).times instance = klass.new 4.times { instance.foo } end end context "the 'at_most(n)' constraint" do it "passes for n invocations where n = 3" do expect_any_instance_of(klass).to receive(:foo).at_most(3).times instance = klass.new 3.times { instance.foo } end it "passes for n invocations where n < 3" do expect_any_instance_of(klass).to receive(:foo).at_most(3).times instance = klass.new 2.times { instance.foo } end it "fails for n invocations where n > 3" do expect_any_instance_of(klass).to receive(:foo).at_most(3).times expect_fast_failure_from(klass.new) do |instance| 4.times { instance.foo } verify instance end end end context "the 'never' constraint" do it "passes for 0 invocations" do expect_any_instance_of(klass).to receive(:foo).never verify_all end it "fails on the first invocation" do expect_any_instance_of(klass).to receive(:foo).never expect_fast_failure_from(klass.new) do |instance| instance.foo end end context "when combined with other expectations" do it "passes when the other expectations are met" do expect_any_instance_of(klass).to receive(:foo).never expect_any_instance_of(klass).to receive(:existing_method).and_return(5) expect(klass.new.existing_method).to eq(5) end it "fails when the other expectations are not met" do expect do expect_any_instance_of(klass).to receive(:foo).never expect_any_instance_of(klass).to receive(:existing_method).and_return(5) verify_all end.to fail_with existing_method_expectation_error_message end end end end end context "when resetting post-verification" do let(:space) { RSpec::Mocks.space } context "existing method" do before(:each) do RSpec::Mocks.space.any_instance_recorder_for(klass) # to force it to be tracked end context "with stubbing" do context "public methods" do before(:each) do allow_any_instance_of(klass).to receive(:existing_method).and_return(1) expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_truthy end it "restores the class to its original state after each example when no instance is created" do verify_all expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) end it "restores the class to its original state after each example when one instance is created" do klass.new.existing_method verify_all expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) end it "restores the class to its original state after each example when more than one instance is created" do klass.new.existing_method klass.new.existing_method verify_all expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey expect(klass.new.existing_method).to eq(existing_method_return_value) end end context "private methods" do before :each do allow_any_instance_of(klass).to receive(:private_method).and_return(:something) verify_all end it "cleans up the backed up method" do expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey end it "restores a stubbed private method after the spec is run" do expect(klass.private_method_defined?(:private_method)).to be_truthy end it "ensures that the restored method behaves as it originally did" do expect(klass.new.send(:private_method)).to eq(:private_method_return_value) end end end context "with expectations" do context "private methods" do before :each do expect_any_instance_of(klass).to receive(:private_method).and_return(:something) klass.new.private_method verify_all end it "cleans up the backed up method" do expect(klass.method_defined?(:__existing_method_without_any_instance__)).to be_falsey end it "restores a stubbed private method after the spec is run" do expect(klass.private_method_defined?(:private_method)).to be_truthy end it "ensures that the restored method behaves as it originally did" do expect(klass.new.send(:private_method)).to eq(:private_method_return_value) end end context "ensures that the subsequent specs do not see expectations set in previous specs" do context "when the instance created after the expectation is set" do it "first spec" do expect_any_instance_of(klass).to receive(:existing_method).and_return(Object.new) klass.new.existing_method end it "second spec" do expect(klass.new.existing_method).to eq(existing_method_return_value) end end context "when the instance created before the expectation is set" do before :each do @instance = klass.new end it "first spec" do expect_any_instance_of(klass).to receive(:existing_method).and_return(Object.new) @instance.existing_method end it "second spec" do expect(@instance.existing_method).to eq(existing_method_return_value) end end end it "ensures that the next spec does not see that expectation" do expect_any_instance_of(klass).to receive(:existing_method).and_return(Object.new) klass.new.existing_method verify_all expect(klass.new.existing_method).to eq(existing_method_return_value) end end end context "with multiple calls to any_instance in the same example" do it "does not prevent the change from being rolled back" do allow_any_instance_of(klass).to receive(:existing_method).and_return(false) allow_any_instance_of(klass).to receive(:existing_method).and_return(true) verify_all expect(klass.new).to respond_to(:existing_method) expect(klass.new.existing_method).to eq(existing_method_return_value) end end it "adds an instance to the current space when stubbed method is invoked" do allow_any_instance_of(klass).to receive(:foo) instance = klass.new instance.foo expect(RSpec::Mocks.space.proxies.keys).to include(instance.object_id) end end context "passing the receiver to the implementation block" do context "when configured to pass the instance" do include_context 'with isolated configuration' before(:each) do RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = true end describe "an any instance stub" do it "passes the instance as the first arg of the implementation block" do instance = klass.new expect { |b| expect_any_instance_of(klass).to receive(:bees).with(:sup, &b) instance.bees(:sup) }.to yield_with_args(instance, :sup) end it "does not pass the instance to and_call_original" do klazz = Class.new do def call(*args) args.first end end expect_any_instance_of(klazz).to receive(:call).and_call_original instance = klazz.new expect(instance.call(:bees)).to be :bees end end describe "an any instance expectation" do it "doesn't effect with" do instance = klass.new expect_any_instance_of(klass).to receive(:bees).with(:sup) instance.bees(:sup) end it "passes the instance as the first arg of the implementation block" do instance = klass.new expect { |b| expect_any_instance_of(klass).to receive(:bees).with(:sup, &b) instance.bees(:sup) }.to yield_with_args(instance, :sup) end end end context "when configured not to pass the instance" do include_context 'with isolated configuration' before(:each) do RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = false end describe "an any instance stub" do it "does not pass the instance to the implementation block" do instance = klass.new expect { |b| expect_any_instance_of(klass).to receive(:bees).with(:sup, &b) instance.bees(:sup) }.to yield_with_args(:sup) end it "does not cause with to fail when the instance is passed" do instance = klass.new expect_any_instance_of(klass).to receive(:bees).with(:faces) instance.bees(:faces) end end end end context 'when used in conjunction with a `dup`' do it "doesn't cause an infinite loop" do skip "This intermittently fails on JRuby" if RUBY_PLATFORM == 'java' allow_any_instance_of(Object).to receive(:some_method) o = Object.new o.some_method expect { o.dup.some_method }.to_not raise_error end it "doesn't bomb if the object doesn't support `dup`" do klazz = Class.new do undef_method :dup end allow_any_instance_of(klazz).to receive(:foo) end it "doesn't fail when dup accepts parameters" do klazz = Class.new do def dup(_) end end allow_any_instance_of(klazz).to receive(:foo) expect { klazz.new.dup('Dup dup dup') }.to_not raise_error end end context "when directed at a method defined on a superclass" do let(:sub_klass) { Class.new(klass) } it "stubs the method correctly" do allow_any_instance_of(klass).to receive(:existing_method).and_return("foo") expect(sub_klass.new.existing_method).to eq "foo" end it "mocks the method correctly" do instance_one = sub_klass.new instance_two = sub_klass.new expect do expect_any_instance_of(klass).to receive(:existing_method) instance_one.existing_method instance_two.existing_method end.to fail_with(/The message 'existing_method' was received by .*#{instance_two.object_id}.* but has already been received by #{instance_one.inspect}/) end end context "when a class overrides Object#method" do let(:http_request_class) { Struct.new(:method, :uri) } it "stubs the method correctly" do allow_any_instance_of(http_request_class).to receive(:existing_method).and_return("foo") expect(http_request_class.new.existing_method).to eq "foo" end it "mocks the method correctly" do expect_any_instance_of(http_request_class).to receive(:existing_method).and_return("foo") expect(http_request_class.new.existing_method).to eq "foo" end end context "when used after the test has finished" do it "restores the original behavior of a stubbed method" do allow_any_instance_of(klass).to receive(:existing_method).and_return(:stubbed_return_value) instance = klass.new expect(instance.existing_method).to eq :stubbed_return_value verify_all expect(instance.existing_method).to eq :existing_method_return_value end it "does not restore a stubbed method not originally implemented in the class" do allow_any_instance_of(::AnyInstanceSpec::ChildClass).to receive(:foo).and_return :result expect(::AnyInstanceSpec::ChildClass.new.foo).to eq :result reset_all expect(::AnyInstanceSpec::ChildClass.new.foo).to eq 'bar' end it "restores the original behaviour, even if the expectation fails" do expect_any_instance_of(klass).to receive(:existing_method).with(1).and_return(:stubbed_return_value) instance = klass.new begin instance.existing_method verify_all rescue RSpec::Mocks::MockExpectationError end reset_all expect(instance.existing_method).to eq :existing_method_return_value end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/argument_matchers_spec.rb000066400000000000000000000460011455767030500242720ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "argument matchers matching" do let(:a_double) { double } after(:each, :reset => true) do reset a_double end describe "boolean" do it "accepts true as boolean" do expect(a_double).to receive(:random_call).with(boolean) a_double.random_call(true) end it "accepts false as boolean" do expect(a_double).to receive(:random_call).with(boolean) a_double.random_call(false) end it "rejects non boolean", :reset => true do expect(a_double).to receive(:random_call).with(boolean) expect { a_double.random_call("false") }.to fail_including "expected: (boolean)" end end describe "kind_of" do it "accepts fixnum as kind_of(Numeric)" do expect(a_double).to receive(:random_call).with(kind_of(Numeric)) a_double.random_call(1) end it "accepts float as kind_of(Numeric)" do expect(a_double).to receive(:random_call).with(kind_of(Numeric)) a_double.random_call(1.5) end it "handles non matching kinds nicely", :reset => true do expect(a_double).to receive(:random_call).with(kind_of(Numeric)) expect { a_double.random_call(true) }.to fail_including "expected: (kind of Numeric)" end it "matches arguments that have defined `kind_of?` to return true" do fix_num = double(:kind_of? => true) expect(a_double).to receive(:random_call).with(kind_of(Numeric)) a_double.random_call(fix_num) end it "handles a class thats overridden ===" do allow(Numeric).to receive(:===) { false } fix_num = double(:kind_of? => true) expect(a_double).to receive(:random_call).with(kind_of(Numeric)) a_double.random_call(fix_num) end end describe "instance_of" do it "accepts float as instance_of(Float)" do expect(a_double).to receive(:random_call).with(instance_of(Float)) a_double.random_call(1.1) end it "does NOT accept float as instance_of(Numeric)" do expect(a_double).not_to receive(:random_call).with(instance_of(Numeric)) a_double.random_call(1.1) end it "does NOT accept integer as instance_of(Numeric)" do expect(a_double).not_to receive(:random_call).with(instance_of(Numeric)) a_double.random_call(1) end it "rejects non numeric", :reset => true do expect(a_double).to receive(:random_call).with(an_instance_of(Numeric)) expect { a_double.random_call("1") }.to fail end it "rejects non string", :reset => true do expect(a_double).to receive(:random_call).with(an_instance_of(String)) expect { a_double.random_call(123) }.to fail end it "handles non matching instances nicely", :reset => true do expect(a_double).to receive(:random_call).with(instance_of(Numeric)) expect { a_double.random_call(1.5) }.to fail_including "expected: (an_instance_of(Numeric))" end end describe "anything" do it "accepts string as anything" do expect(a_double).to receive(:random_call).with("a", anything, "c") a_double.random_call("a", "whatever", "c") end it "doesn't accept no arguments" do expect(a_double).to_not receive(:random_call).with(anything) a_double.random_call end it "handles non matching instances nicely", :reset => true do expect(a_double).to receive(:random_call).with(anything) expect { a_double.random_call }.to fail_including "expected: (anything)" end end describe "duck_type" do it "matches duck type with one method" do expect(a_double).to receive(:random_call).with(duck_type(:length)) a_double.random_call([]) end it "matches duck type with two methods" do expect(a_double).to receive(:random_call).with(duck_type(:abs, :div)) a_double.random_call(1) end it "rejects goose when expecting a duck", :reset => true do expect(a_double).to receive(:random_call).with(duck_type(:abs, :div)) expect { a_double.random_call("I don't respond to :abs or :div") }.to fail_including "expected: (duck_type(:abs, :div))" end end describe "any_args" do context "as the only arg passed to `with`" do before { expect(a_double).to receive(:random_call).with(any_args) } it "matches no args" do a_double.random_call end it "matches one arg" do a_double.random_call("a string") end it "matches many args" do a_double.random_call("a string", :other, 3) end end context "as the last of three args" do before { expect(a_double).to receive(:random_call).with(1, /foo/, any_args) } it "matches a call of two args when it matches the first two explicit args" do a_double.random_call(1, "food") end it "matches a call of three args when it matches the first two explicit args" do a_double.random_call(1, "food", :more) end it "matches a call of four args when it matches the first two explicit args" do a_double.random_call(1, "food", :more, :args) end it "does not match a call where the first two args do not match", :reset => true do expect { a_double.random_call(1, "bar", 2, 3) }.to fail_including "expected: (1, /foo/, *(any args))" end it "does not match a call of no args", :reset => true do expect { a_double.random_call }.to fail_including "expected: (1, /foo/, *(any args))" end end context "as the first of three args" do before { expect(a_double).to receive(:random_call).with(any_args, 1, /foo/) } it "matches a call of two args when it matches the last two explicit args" do a_double.random_call(1, "food") end it "matches a call of three args when it matches the last two explicit args" do a_double.random_call(nil, 1, "food") end it "matches a call of four args when it matches the last two explicit args" do a_double.random_call(:some, :args, 1, "food") end it "does not match a call where the last two args do not match", :reset => true do expect { a_double.random_call(1, "bar", 2, 3) }.to fail_including "expected: (*(any args), 1, /foo/)" end it "does not match a call of no args", :reset => true do expect { a_double.random_call }.to fail_including "expected: (*(any args), 1, /foo/)" end end context "as the middle of three args" do before { expect(a_double).to receive(:random_call).with(1, any_args, /foo/) } it "matches a call of two args when it matches the first and last args" do a_double.random_call(1, "food") end it "matches a call of three args when it matches the first and last args" do a_double.random_call(1, nil, "food") end it "matches a call of four args when it matches the first and last args" do a_double.random_call(1, :some, :args, "food") end it "does not match a call where the first and last args do not match", :reset => true do expect { a_double.random_call(nil, "bar", 2, 3) }.to fail_including "expected: (1, *(any args), /foo/)" end it "does not match a call of no args", :reset => true do expect { a_double.random_call }.to fail_including "expected: (1, *(any args), /foo/)" end end context "when passed twice" do it 'immediately signals that this is invalid', :reset => true do expect { expect(a_double).to receive(:random_call).with(any_args, 1, any_args) }.to raise_error(ArgumentError, /any_args/) end end end describe "no_args" do it "matches no args against no_args" do expect(a_double).to receive(:random_call).with(no_args) a_double.random_call end it "fails no_args with one arg", :reset => true do expect(a_double).to receive(:msg).with(no_args) expect { a_double.msg(37) }.to fail_including "expected: (no args)" end context "when passed with other arguments" do it 'immediately signals that this is invalid', :reset => true do expect { expect(a_double).to receive(:random_call).with(no_args, 3) }.to raise_error(ArgumentError, /no_args/) end end end describe "hash_including" do it "matches hash with hash_including same hash" do expect(a_double).to receive(:random_call).with(hash_including(:a => 1)) a_double.random_call(:a => 1) end it "fails hash_including with missing key", :reset => true do expect(a_double).to receive(:random_call).with(hash_including(:a => 1)) expect { a_double.random_call(:a => 2) }.to fail_including "expected: (hash_including(:a=>1))" end end describe "hash_excluding" do it "matches hash with hash_excluding same hash" do expect(a_double).to receive(:random_call).with(hash_excluding(:a => 1)) a_double.random_call(:a => 2) end it "handles non matching instances nicely", :reset => true do expect(a_double).to receive(:random_call).with(hash_excluding(:a => 1)) expect { a_double.random_call(:a => 1) }.to fail_including "expected: (hash_not_including(:a=>1))" end end describe "array_including" do it "matches array with array_including same array" do expect(a_double).to receive(:random_call).with(array_including(1, 2)) a_double.random_call([1, 2]) end it "matches array with array_including using fuzzymatcher", :reset => true do expect(a_double).to receive(:random_call).with(array_including(a_value > 1)) a_double.random_call([1, 2]) end it "fails array_including when args aren't array", :reset => true do expect(a_double).to receive(:msg).with(array_including(1, 2, 3)) expect { a_double.msg(1, 2, 3) }.to fail_including "expected: (array_including(1, 2, 3))" end it "fails array_including when arg doesn't contain all elements", :reset => true do expect(a_double).to receive(:msg).with(array_including(1, 2, 3)) expect { a_double.msg([1, 2]) }.to fail_including "expected: (array_including(1, 2, 3))" end end describe "array_excluding" do it "matches array with array_excluding different array" do expect(a_double).to receive(:random_call).with(array_excluding(3, 4)) a_double.random_call([1, 2]) end it "fails array_excluding when is the same array", :reset => true do expect(a_double).to receive(:msg).with(array_excluding(1, 2, 3)) expect { a_double.msg(1, 2, 3) }.to fail_including "expected: (array_excluding(1, 2, 3))" end it "fails array_excluding when arg contains some elements", :reset => true do expect(a_double).to receive(:msg).with(array_excluding(2, 3)) expect { a_double.msg([1, 2]) }.to fail_including "expected: (array_excluding(2, 3))" end it "matches array_excluding when using the fuzzy matcher", :reset => true do expect(a_double).to receive(:msg).with(array_excluding(a_value > 3)) a_double.msg([1, 2]) end it "fails array_excluding when using the fuzzy matcher", :reset => true do expect(a_double).to receive(:msg).with(array_excluding(a_value > 1)) expect { a_double.msg([1, 2]) }.to fail_including "expected: (array_excluding(a value > 1))" end end context "handling arbitrary matchers" do it "matches any arbitrary object using #===" do matcher = double expect(matcher).to receive(:===).with(4).and_return(true) expect(a_double).to receive(:foo).with(matcher) a_double.foo(4) end it "matches against a Matcher", :reset => true do expect(a_double).to receive(:msg).with(equal(3)) # This spec is generating warnings on 1.8.7, not sure why so # this does with_isolated_stderr to kill them. @samphippen 3rd Jan 2013. expect { with_isolated_stderr { a_double.msg(37) } }.to fail_including "expected: (equal 3)" end it "fails when given an arbitrary object that returns false from #===", :reset => true do matcher = double expect(matcher).to receive(:===).with(4).at_least(:once).and_return(false) expect(a_double).to receive(:foo).with(matcher) expect { a_double.foo(4) }.to fail end end context "handling objects with a wrong definition of `==` that raises errors for other types" do Color = Struct.new(:r, :g, :b) do def ==(other) other.r == r && other.g == g && other.b == b end end before(:context) do expect { Color.new(0, 0, 0) == Object.new }.to raise_error(NoMethodError) end it 'matches against an equal instance of the same type' do expect(a_double).to receive(:random_call).with(Color.new(0, 0, 0)) a_double.random_call(Color.new(0, 0, 0)) end it 'fails when matched against an unequal instance of the same class', :reset do expect(a_double).to receive(:random_call).with(Color.new(0, 0, 0)) expect { a_double.random_call(Color.new(0, 1, 0)) }.to fail end it 'can match multiple instances of the type against multiple equal instances of the type' do expect(a_double).to receive(:random_call).with( Color.new(0, 0, 0), Color.new(0, 1, 0) ) a_double.random_call( Color.new(0, 0, 0), Color.new(0, 1, 0) ) end end context "handling non-matcher arguments" do it "matches string against regexp" do expect(a_double).to receive(:random_call).with(/bcd/) a_double.random_call("abcde") end it "matches regexp against regexp" do expect(a_double).to receive(:random_call).with(/bcd/) a_double.random_call(/bcd/) end it "fails if regexp does not match submitted string", :reset => true do expect(a_double).to receive(:random_call).with(/bcd/) expect { a_double.random_call("abc") }.to fail end it "fails if regexp does not match submitted regexp", :reset => true do expect(a_double).to receive(:random_call).with(/bcd/) expect { a_double.random_call(/bcde/) }.to fail end it "matches against a hash submitted and received by value" do expect(a_double).to receive(:random_call).with(:a => "a", :b => "b") a_double.random_call(:a => "a", :b => "b") end it "matches against a hash submitted as keyword arguments a and received as a positional argument (in both Ruby 2 and Ruby 3)" do opts = {:a => "a", :b => "b"} expect(a_double).to receive(:random_call).with(opts) a_double.random_call(:a => "a", :b => "b") end if RUBY_VERSION >= "3" it "fails to match against a hash submitted as a positional argument and received as keyword arguments in Ruby 3.0 or later", :reset => true do opts = {:a => "a", :b => "b"} expect(a_double).to receive(:random_call).with(:a => "a", :b => "b") expect do a_double.random_call(opts) end.to fail_with(/expected: \(\{(:a=>\"a\", :b=>\"b\"|:b=>\"b\", :a=>\"a\")\}\)/) end else it "matches against a hash submitted as a positional argument and received as keyword arguments in Ruby 2.7 or before" do opts = {:a => "a", :b => "b"} expect(a_double).to receive(:random_call).with(:a => "a", :b => "b") a_double.random_call(opts) end end it "fails for a hash w/ wrong values", :reset => true do expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") expect do a_double.random_call(:a => "b", :c => "e") end.to fail_with(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/) end it "fails for a hash w/ wrong keys", :reset => true do expect(a_double).to receive(:random_call).with(:a => "b", :c => "d") expect do a_double.random_call("a" => "b", "c" => "d") end.to fail_with(/expected: \(\{(:a=>\"b\", :c=>\"d\"|:c=>\"d\", :a=>\"b\")\}\)/) end it "matches a class against itself" do expect(a_double).to receive(:foo).with(Float) a_double.foo(Float) end it "fails a class against an unrelated class", :reset => true do expect(a_double).to receive(:foo).with(Float) expect { a_double.foo(Hash) }.to fail end it "matches a class against an instance of itself" do expect(a_double).to receive(:foo).with(Float) a_double.foo(3.3) end it "fails a class against an object of a different type", :reset => true do expect(a_double).to receive(:foo).with(Float) expect { a_double.foo(3) }.to fail end it "fails with zero arguments", :reset => true do expect do expect(a_double).to receive(:msg).with { |arg| expect(arg).to eq :received } end.to raise_error(ArgumentError, /must have at least one argument/) end it "fails with sensible message when args respond to #description", :reset => true do arg = double(:description => nil, :inspect => "my_thing") expect(a_double).to receive(:msg).with(3) expect { a_double.msg arg }.to fail_including "got: (my_thing)" end it "fails with sensible message when arg#description is nil", :reset => true do arg = double(:description => nil, :inspect => "my_thing") expect(a_double).to receive(:msg).with(arg) expect { a_double.msg 3 }.to fail_including "expected: (my_thing)" end it "fails with sensible message when arg#description is blank", :reset => true do arg = double(:description => "", :inspect => "my_thing") expect(a_double).to receive(:msg).with(arg) expect { a_double.msg 3 }.to fail_including "expected: (my_thing)" end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/array_including_matcher_spec.rb000066400000000000000000000051431455767030500254410ustar00rootroot00000000000000module RSpec module Mocks module ArgumentMatchers RSpec.describe ArrayIncludingMatcher do it "describes itself properly" do expect(ArrayIncludingMatcher.new([1, 2, 3]).description).to eq "array_including(1, 2, 3)" end it "describes passed matchers" do description = array_including(fake_matcher(Object.new)).description expect(description).to include(MatcherHelpers.fake_matcher_description) end context "passing" do it "matches the same array" do expect(array_including([1, 2, 3])).to be === [1, 2, 3] end it "matches the same array, specified without square brackets" do expect(array_including(1, 2, 3)).to be === [1, 2, 3] end it "matches the same array, specified without square brackets" do expect(array_including(1, 2, 3)).to be === [1, 2, 3] end it "matches the same array, which includes nested arrays" do expect(array_including([1, 2], 3, 4)).to be === [[1, 2], 3, 4] end it "works with duplicates in expected" do expect(array_including(1, 1, 2, 3)).to be === [1, 2, 3] end it "works with duplicates in actual" do expect(array_including(1, 2, 3)).to be === [1, 1, 2, 3] end it "is composable with other matchers" do klass = Class.new dbl = double expect(dbl).to receive(:a_message).with(3, array_including(instance_of(klass))) dbl.a_message(3, [1, klass.new, 4]) end # regression check it "is composable when nested" do expect(array_including(1, array_including(2, 3), 4)).to be === [1, [2, 3], 4] expect([[1, 2], 3, 4]).to match array_including(array_including(1, 2), 3, 4) expect([1,[1,2]]).to match array_including(1, array_including(1,2)) end end context "failing" do it "fails when not all the entries in the expected are present" do expect(array_including(1, 2, 3, 4, 5)).not_to be === [1, 2] end it "fails when passed a composed matcher is passed and not satisfied" do with_unfulfilled_double do |dbl| expect { klass = Class.new expect(dbl).to receive(:a_message).with(3, array_including(instance_of(klass))) dbl.a_message(3, [1, 4]) }.to fail_with(/expected: \(3, array_including\(an_instance_of\(\)\)\)/) end end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/at_least_spec.rb000066400000000000000000000122211455767030500223530ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "at_least" do before(:each) { @double = double } it "fails if method is never called" do expect(@double).to receive(:do_something).at_least(4).times expect { verify @double }.to raise_error(/expected: at least 4 times.*received: 0 times/m) end it "fails when called less than n times" do expect(@double).to receive(:do_something).at_least(4).times @double.do_something @double.do_something @double.do_something expect { verify @double }.to raise_error(/expected: at least 4 times.*received: 3 times/m) end it "fails when at least once method is never called" do expect(@double).to receive(:do_something).at_least(:once) expect { verify @double }.to raise_error(/expected: at least 1 time.*received: 0 times/m) end it "fails when at least twice method is called once" do expect(@double).to receive(:do_something).at_least(:twice) @double.do_something expect { verify @double }.to raise_error(/expected: at least 2 times.*received: 1 time/m) end it "fails when at least twice method is never called" do expect(@double).to receive(:do_something).at_least(:twice) expect { verify @double }.to raise_error(/expected: at least 2 times.*received: 0 times/m) end it "fails when at least thrice method is called less than three times" do expect(@double).to receive(:do_something).at_least(:thrice) @double.do_something @double.do_something expect { verify @double }.to raise_error(/expected: at least 3 times.*received: 2 times/m) end it "passes when at least n times method is called exactly n times" do expect(@double).to receive(:do_something).at_least(4).times @double.do_something @double.do_something @double.do_something @double.do_something verify @double end it "passes when at least n times method is called n plus 1 times" do expect(@double).to receive(:do_something).at_least(4).times @double.do_something @double.do_something @double.do_something @double.do_something @double.do_something verify @double end it "passes when at least once method is called once" do expect(@double).to receive(:do_something).at_least(:once) @double.do_something verify @double end it "passes when at least once method is called twice" do expect(@double).to receive(:do_something).at_least(:once) @double.do_something @double.do_something verify @double end it "passes when at least twice method is called three times" do expect(@double).to receive(:do_something).at_least(:twice) @double.do_something @double.do_something @double.do_something verify @double end it "passes when at least twice method is called twice" do expect(@double).to receive(:do_something).at_least(:twice) @double.do_something @double.do_something verify @double end it "passes when at least thrice method is called three times" do expect(@double).to receive(:do_something).at_least(:thrice) @double.do_something @double.do_something @double.do_something verify @double end it "passes when at least thrice method is called four times" do expect(@double).to receive(:do_something).at_least(:thrice) @double.do_something @double.do_something @double.do_something @double.do_something verify @double end it "returns the value given by a block when the at least once method is called" do expect(@double).to receive(:to_s).at_least(:once) { "testing" } expect(@double.to_s).to eq "testing" verify @double end context "when sent with 0" do it "outputs a removal message" do expect { expect(@double).to receive(:do_something).at_least(0).times }.to raise_error(/has been removed/) end end it "uses a stub value if no value set" do allow(@double).to receive_messages(:do_something => 'foo') expect(@double).to receive(:do_something).at_least(:once) expect(@double.do_something).to eq 'foo' expect(@double.do_something).to eq 'foo' end it "prefers its own return value over a stub" do allow(@double).to receive_messages(:do_something => 'foo') expect(@double).to receive(:do_something).at_least(:once).and_return('bar') expect(@double.do_something).to eq 'bar' expect(@double.do_something).to eq 'bar' end context "when called with negative expectation" do it "raises an error" do expect { expect(@double).not_to receive(:do_something).at_least(:thrice) }.to raise_error(/`count` is not supported with negative message expectations/) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/at_most_spec.rb000066400000000000000000000106011455767030500222250ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "at_most" do before(:each) do @double = double end it "passes when at_most(n) is called exactly 1 time" do expect(@double).to receive(:do_something).at_most(1).time @double.do_something verify @double end it "passes when at_most(n) is called exactly n times" do expect(@double).to receive(:do_something).at_most(2).times @double.do_something @double.do_something verify @double end it "passes when at_most(n) is called less than n times" do expect(@double).to receive(:do_something).at_most(2).times @double.do_something verify @double end it "passes when at_most(n) is never called" do expect(@double).to receive(:do_something).at_most(2).times verify @double end it "passes when at_most(:once) is called once" do expect(@double).to receive(:do_something).at_most(:once) @double.do_something verify @double end it "passes when at_most(:once) is never called" do expect(@double).to receive(:do_something).at_most(:once) verify @double end it "passes when at_most(:twice) is called once" do expect(@double).to receive(:do_something).at_most(:twice) @double.do_something verify @double end it "passes when at_most(:twice) is called twice" do expect(@double).to receive(:do_something).at_most(:twice) @double.do_something @double.do_something verify @double end it "passes when at_most(:twice) is never called" do expect(@double).to receive(:do_something).at_most(:twice) verify @double end it "passes when at_most(:thrice) is called less than three times" do expect(@double).to receive(:do_something).at_most(:thrice) @double.do_something verify @double end it "passes when at_most(:thrice) is called thrice" do expect(@double).to receive(:do_something).at_most(:thrice) @double.do_something @double.do_something @double.do_something verify @double end it "returns the value given by a block when at_most(:once) method is called" do expect(@double).to receive(:to_s).at_most(:once) { "testing" } expect(@double.to_s).to eq "testing" verify @double end it "fails fast when at_most(n) times method is called n plus 1 times" do expect(@double).to receive(:do_something).at_most(2).times @double.do_something @double.do_something expect_fast_failure_from(@double, /expected: at most 2 times.*received: 3 times/m) do @double.do_something end end it "fails fast when at_most(n) times method is called n plus 1 time" do expect(@double).to receive(:do_something).at_most(1).time @double.do_something expect_fast_failure_from(@double, /expected: at most 1 time.*received: 2 times/m) do @double.do_something end end it "fails fast when at_most(:once) and is called twice" do expect(@double).to receive(:do_something).at_most(:once) @double.do_something expect_fast_failure_from(@double, /expected: at most 1 time.*received: 2 times/m) do @double.do_something end end it "fails fast when at_most(:twice) and is called three times" do expect(@double).to receive(:do_something).at_most(:twice) @double.do_something @double.do_something expect_fast_failure_from(@double, /expected: at most 2 times.*received: 3 times/m) do @double.do_something end end it "fails fast when at_most(:thrice) and is called four times" do expect(@double).to receive(:do_something).at_most(:thrice) @double.do_something @double.do_something @double.do_something expect_fast_failure_from(@double, /expected: at most 3 times.*received: 4 times/m) do @double.do_something end end context "when called with negative expectation" do it "raises an error" do expect { expect(@double).not_to receive(:do_something).at_most(:thrice) }.to raise_error(/`count` is not supported with negative message expectations/) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/before_all_spec.rb000066400000000000000000000035471455767030500226640ustar00rootroot00000000000000require 'support/before_all_shared_example_group' RSpec.describe "Using rspec-mocks features in before(:all) blocks" do describe "#stub_const" do include_examples "fails in a before(:all) block" do def use_rspec_mocks stub_const("SomeNewConst", Class.new) end it 'does not stub the const' do expect(defined?(SomeNewConst)).to be_falsey end end end describe "#hide_const(for an undefined const)" do include_examples "fails in a before(:all) block" do def use_rspec_mocks hide_const("Foo") end end end describe "#hide_const(for a defined const)" do include_examples "fails in a before(:all) block" do def use_rspec_mocks hide_const("Float") end it 'does not hide the const' do expect(defined?(Float)).to be_truthy end end end describe "allow(...).to receive_message_chain" do include_examples "fails in a before(:all) block" do def use_rspec_mocks allow(Object).to receive_message_chain(:foo, :bar) end end end describe "#expect(...).to receive" do include_examples "fails in a before(:all) block" do def use_rspec_mocks expect(Object).to receive(:foo) end end end describe "#allow(...).to receive" do include_examples "fails in a before(:all) block" do def use_rspec_mocks allow(Object).to receive(:foo) end end end describe "#expect_any_instance_of(...).to receive" do include_examples "fails in a before(:all) block" do def use_rspec_mocks expect_any_instance_of(Object).to receive(:foo) end end end describe "#allow_any_instance_of(...).to receive" do include_examples "fails in a before(:all) block" do def use_rspec_mocks allow_any_instance_of(Object).to receive(:foo) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/block_return_value_spec.rb000066400000000000000000000044431455767030500244530ustar00rootroot00000000000000RSpec.describe "a double declaration with a block handed to:" do describe "expect(...).to receive" do it "returns the value of executing the block" do obj = Object.new expect(obj).to receive(:foo) { 'bar' } expect(obj.foo).to eq('bar') end it "works when a multi-return stub has already been set" do obj = Object.new return_value = Object.new allow(obj).to receive(:foo).and_return(return_value, nil) expect(obj).to receive(:foo) { return_value } expect(obj.foo).to be(return_value) end end describe "allow(...).to receive" do it "returns the value of executing the block" do obj = Object.new allow(obj).to receive(:foo) { 'bar' } expect(obj.foo).to eq('bar') end # The "receives a block" part is important: 1.8.7 has a bug that reports the # wrong arity when a block receives a block. it 'forwards all given args to the block, even when it receives a block' do obj = Object.new yielded_args = [] allow(obj).to receive(:foo) { |*args, &_| yielded_args << args } obj.foo(1, 2, 3) expect(yielded_args).to eq([[1, 2, 3]]) end end describe "with" do it "returns the value of executing the block" do obj = Object.new allow(obj).to receive(:foo).with('baz') { 'bar' } expect(obj.foo('baz')).to eq('bar') end it "returns the value of executing the block with given argument" do obj = Object.new allow(obj).to receive(:foo).with('baz') { |x| 'bar' + x } expect(obj.foo('baz')).to eq('barbaz') end end %w[once twice].each do |method| describe method do it "returns the value of executing the block" do obj = Object.new allow(obj).to receive(:foo).send(method) { 'bar' } expect(obj.foo).to eq('bar') end end end describe 'ordered' do it "returns the value of executing the block" do obj = Object.new expect_warning_with_call_site(__FILE__, __LINE__ + 1) allow(obj).to receive(:foo).ordered { 'bar' } expect(obj.foo).to eq('bar') end end describe "times" do it "returns the value of executing the block" do obj = Object.new allow(obj).to receive(:foo).at_least(1).time { 'bar' } expect(obj.foo('baz')).to eq('bar') end end end rspec-mocks-3.13.0/spec/rspec/mocks/combining_implementation_instructions_spec.rb000066400000000000000000000152171455767030500304650ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "Combining implementation instructions" do it 'can combine and_yield and and_return' do dbl = double allow(dbl).to receive(:foo).and_yield(5).and_return(3) expect { |b| expect(dbl.foo(&b)).to eq(3) }.to yield_with_args(5) end describe "combining and_yield, a block implementation and and_return" do def verify_combined_implementation dbl = double (yield dbl).and_yield(5).and_return(3) expect { |b| expect(dbl.foo(:arg, &b)).to eq(3) }.to yield_with_args(5) expect(@block_called).to be_truthy end it 'works when passing a block to `stub`' do verify_combined_implementation do |dbl| allow(dbl).to receive(:foo) { @block_called = true } end end it 'works when passing a block to `with`' do verify_combined_implementation do |dbl| allow(dbl).to receive(:foo).with(:arg) { @block_called = true } end end it 'works when passing a block to `exactly`' do verify_combined_implementation do |dbl| expect(dbl).to receive(:foo).exactly(:once) { @block_called = true } end end it 'works when passing a block to `at_least`' do verify_combined_implementation do |dbl| expect(dbl).to receive(:foo).at_least(:once) { @block_called = true } end end it 'works when passing a block to `at_most`' do verify_combined_implementation do |dbl| expect(dbl).to receive(:foo).at_most(:once) { @block_called = true } end end it 'works when passing a block to `times`' do verify_combined_implementation do |dbl| expect(dbl).to receive(:foo).exactly(1).time { @block_called = true } end end it 'works when passing a block to `once`' do verify_combined_implementation do |dbl| expect(dbl).to receive(:foo).once { @block_called = true } end end it 'works when passing a block to `twice`' do the_double = nil verify_combined_implementation do |dbl| the_double = dbl expect(dbl).to receive(:foo).twice { @block_called = true } end the_double.foo { |_| } # to ensure it is called twice end it 'works when passing a block to `ordered`' do verify_combined_implementation do |dbl| expect(dbl).to receive(:foo).ordered { @block_called = true } end end end it 'can combine and_yield and and_raise' do dbl = double allow(dbl).to receive(:foo).and_yield(5).and_raise("boom") expect { |b| expect { dbl.foo(&b) }.to raise_error("boom") }.to yield_with_args(5) end it 'can combine and_yield, a block implementation and and_raise' do dbl = double block_called = false allow(dbl).to receive(:foo) { block_called = true }.and_yield(5).and_raise("boom") expect { |b| expect { dbl.foo(&b) }.to raise_error("boom") }.to yield_with_args(5) expect(block_called).to be_truthy end it 'can combine and_yield and and_throw' do dbl = double allow(dbl).to receive(:foo).and_yield(5).and_throw(:bar) expect { |b| expect { dbl.foo(&b) }.to throw_symbol(:bar) }.to yield_with_args(5) end it 'can combine and_yield, a block implementation and and_throw' do dbl = double block_called = false allow(dbl).to receive(:foo) { block_called = true }.and_yield(5).and_throw(:bar) expect { |b| expect { dbl.foo(&b) }.to throw_symbol(:bar) }.to yield_with_args(5) expect(block_called).to be_truthy end describe "a double that already has a terminal `and_return(x)` action" do let(:the_dbl) { double } let(:stubbed_double) { allow(the_dbl).to receive(:foo) } before { stubbed_double.and_return(1) } it 'allows the terminal action to be overridden to `and_return(y)`' do stubbed_double.and_return(3) expect(the_dbl.foo).to eq(3) end it 'allows the terminal action to be overridden to `and_raise(y)`' do stubbed_double.and_raise("boom") expect { the_dbl.foo }.to raise_error("boom") end it 'allows the terminal action to be overridden to `and_throw(y)`' do stubbed_double.and_throw(:bar) expect { the_dbl.foo }.to throw_symbol(:bar) end end describe "a double that already has a terminal block action" do let(:the_dbl) { double } let(:stubbed_double) { allow(the_dbl).to receive(:foo) } it "allows the block action to be overridden" do allow(RSpec).to receive(:warning) stubbed_double.with(:arg) { :with_block } stubbed_double.at_least(:once) { :at_least_block } expect(the_dbl.foo(:arg)).to eq(:at_least_block) end end it 'warns when the inner implementation block is overridden' do expect(RSpec).to receive(:warning).with(/overriding a previous stub implementation of `foo`.*#{__FILE__}:#{__LINE__ + 1}/) allow(double).to receive(:foo).with(:arg) { :with_block }.at_least(:once) { :at_least_block } end it "does not warn about overriding the stub when `:with` is chained off the block" do expect(RSpec).not_to receive(:warning) obj = Object.new stub = allow(obj).to receive(:foo) {} stub.with(1) end it 'can combine and_call_original, with, and_return' do obj = Struct.new(:value).new('original') allow(obj).to receive(:value).and_call_original allow(obj).to receive(:value).with(:arg).and_return('value') expect(obj.value).to eq 'original' expect(obj.value(:arg)).to eq 'value' end it 'raises an error if `and_call_original` is followed by any other instructions' do allow(RSpec).to receive(:warning) dbl = [1, 2, 3] stubbed = allow(dbl).to receive(:size) stubbed.and_call_original msg_fragment = /cannot be modified further/ expect { stubbed.and_yield }.to raise_error(msg_fragment) expect { stubbed.and_return(1) }.to raise_error(msg_fragment) expect { stubbed.and_raise("a") }.to raise_error(msg_fragment) expect { stubbed.and_throw(:bar) }.to raise_error(msg_fragment) expect { stubbed.once {} }.to raise_error(msg_fragment) expect(dbl.size).to eq(3) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/configuration_spec.rb000066400000000000000000000145271455767030500234410ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe Configuration do let(:config) { Configuration.new } let(:mod_1) { Module.new } let(:mod_2) { Module.new } def instance_methods_of(_mod) mod_1.instance_methods.map(&:to_sym) end it 'adds stub and should_receive to the given modules' do expect(instance_methods_of(mod_1)).not_to include(:stub, :should_receive) expect(instance_methods_of(mod_2)).not_to include(:stub, :should_receive) config.add_stub_and_should_receive_to(mod_1, mod_2) expect(instance_methods_of(mod_1)).to include(:stub, :should_receive) expect(instance_methods_of(mod_2)).to include(:stub, :should_receive) end shared_examples "configuring the syntax" do def sandboxed orig_syntax = RSpec::Mocks.configuration.syntax yield ensure configure_syntax(orig_syntax) end around(:each) { |ex| sandboxed(&ex) } let(:dbl) { double } let(:should_methods) { [:should_receive, :stub, :should_not_receive] } let(:should_class_methods) { [:any_instance] } let(:expect_methods) { [:receive, :allow, :expect_any_instance_of, :allow_any_instance_of] } it 'defaults to enabling both the :should and :expect syntaxes' do # This is kinda a hack, but since we want to enforce use of # the expect syntax within our specs here, we have modified the # config setting, which makes it hard to get at the original # default value. in spec_helper.rb we store the default value # in $default_rspec_mocks_syntax so we can use it here. RSpec::Mocks.configuration.syntax = $default_rspec_mocks_syntax expect(dbl).to respond_to(*should_methods) expect(self).to respond_to(*expect_methods) end context 'when configured to :expect' do before { configure_syntax :expect } it 'removes the should methods from every object' do expect(dbl).not_to respond_to(*should_methods) end it 'removes `any_instance` from every class' do expect(Class.new).not_to respond_to(*should_class_methods) end it 'adds the expect methods to the example group context' do expect(self).to respond_to(*expect_methods) end it 'reports that the syntax is :expect' do expect(configured_syntax).to eq([:expect]) end it 'is a no-op when configured a second time' do expect(Syntax.default_should_syntax_host).not_to receive(:method_undefined) expect(::RSpec::Mocks::ExampleMethods).not_to receive(:method_added) configure_syntax :expect end end context 'when configured to :should' do before { configure_syntax :should } it 'adds the should methods to every object' do expect(dbl).to respond_to(*should_methods) end it 'adds `any_instance` to every class' do expect(Class.new).to respond_to(*should_class_methods) end it 'removes the expect methods from the example group context' do expect(self).not_to respond_to(*expect_methods) end it 'reports that the syntax is :should' do expect(configured_syntax).to eq([:should]) end it "does not warn about the should syntax" do RSpec.should_not_receive(:deprecate) Object.new.should_not_receive(:bees) end it 'is a no-op when configured a second time' do Syntax.default_should_syntax_host.should_not_receive(:method_added) ::RSpec::Mocks::ExampleMethods.should_not_receive(:method_undefined) configure_syntax :should end end context 'when configured to [:should, :expect]' do before { configure_syntax [:should, :expect] } it 'adds the should methods to every object' do expect(dbl).to respond_to(*should_methods) end it 'adds `any_instance` to every class' do expect(Class.new).to respond_to(*should_class_methods) end it 'adds the expect methods to the example group context' do expect(self).to respond_to(*expect_methods) end it 'reports that both syntaxes are enabled' do expect(configured_syntax).to eq([:should, :expect]) end it "does not warn about the should syntax" do expect(RSpec).not_to receive(:deprecate) expect(Object.new).not_to receive(:bees) end end end describe "configuring rspec-mocks directly" do it_behaves_like "configuring the syntax" do def configure_syntax(syntax) RSpec::Mocks.configuration.syntax = syntax end def configured_syntax RSpec::Mocks.configuration.syntax end def configure_default_syntax RSpec::Mocks.configuration.reset_syntaxes_to_default end end end describe "configuring using the rspec-core config API" do it_behaves_like "configuring the syntax" do def configure_syntax(syntax) RSpec.configure do |rspec| rspec.mock_with :rspec do |c| c.syntax = syntax end end end def configured_syntax RSpec.configure do |rspec| rspec.mock_with :rspec do |c| return c.syntax end end end def configure_default_syntax RSpec.configure do |rspec| rspec.mock_with :rspec do |c| c.reset_syntaxes_to_default end end end end end describe "#when_declaring_verifying_double" do include_context 'with isolated configuration' it "captures the supplied blocks" do block = proc { |ref| ref } block2 = proc { |ref| ref } RSpec.configuration.mock_with :rspec do |config| config.before_verifying_doubles(&block) config.when_declaring_verifying_double(&block2) expect(config.verifying_double_callbacks).to eq [block, block2] end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/diffing_spec.rb000066400000000000000000000301711455767030500221710ustar00rootroot00000000000000require "spec_helper" require "pp" RSpec.describe "Diffs printed when arguments don't match" do include RSpec::Support::Spec::DiffHelpers before do allow(RSpec::Mocks.configuration).to receive(:color?).and_return(false) end context "with a non matcher object" do it "does not print a diff when single line arguments are mismatched" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with("some string") expect { d.foo("this other string") }.to fail_with(a_string_excluding("Diff:")) end end it "does not print a diff when differ returns a string of only whitespace" do differ = instance_double(RSpec::Support::Differ, :diff => " \n \t ") allow(RSpec::Support::Differ).to receive_messages(:new => differ) with_unfulfilled_double do |d| expect(d).to receive(:foo).with("some string\nline2") expect { d.foo("this other string") }.to fail_with(a_string_excluding("Diff:")) end end it "does not print a diff when differ returns a string of only whitespace when colour is enabled" do allow(RSpec::Mocks.configuration).to receive(:color?) { true } differ = instance_double(RSpec::Support::Differ, :diff => "\e[0m\n \t\e[0m") allow(RSpec::Support::Differ).to receive_messages(:new => differ) with_unfulfilled_double do |d| expect(d).to receive(:foo).with("some string\nline2") expect { d.foo("this other string") }.to fail_with(a_string_excluding("Diff:")) end end it "prints a diff of the strings for individual mismatched multi-line string arguments" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with("some string\nline2") expect { d.foo("this other string") }.to fail_with("# received :foo with unexpected arguments\n" \ " expected: (\"some string\\nline2\")\n got: (\"this other string\")\n" \ "Diff:\n@@ -1,3 +1,2 @@\n-some string\n-line2\n+this other string\n") end end it "prints a diff of the args lists for multiple mismatched string arguments" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with("some string\nline2", "some other string") expect { d.foo("this other string") }.to fail_with("# received :foo with unexpected arguments\n" \ " expected: (\"some string\\nline2\", \"some other string\")\n" \ " got: (\"this other string\")\nDiff:\n@@ -1,3 +1,2 @@\n-some string\\nline2\n-some other string\n+this other string\n") end end it "does not print a diff when multiple single-line string arguments are mismatched" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with("some string", "some other string") expect { d.foo("this other string", "a fourth string") }.to fail_with(a_string_excluding("Diff:")) end end let(:expected_hash) { {:baz => :quz, :foo => :bar } } let(:actual_hash) { {:bad => :hash} } it "prints a diff with hash args" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with(expected_hash) expect { d.foo({:bad => :hash}) }.to fail_with(/\A# received :foo with unexpected arguments\n expected: \(#{hash_regex_inspect expected_hash}\)\n got: \(#{hash_regex_inspect actual_hash}\)\nDiff:\n@@ #{Regexp.escape one_line_header} @@\n\-\[#{hash_regex_inspect expected_hash}\]\n\+\[#{hash_regex_inspect actual_hash}\]\n\z/) end end it "prints a diff with an expected hash arg and a non-hash actual arg" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with(expected_hash) expect { d.foo(Object.new) }.to fail_with(/-\[#{hash_regex_inspect expected_hash}\].*\+\[#\]/m) end end context 'with keyword arguments on normal doubles' do if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash? eval <<-'RUBY', nil, __FILE__, __LINE__ + 1 it "prints a diff when keyword argument were expected but got an option hash (using splat)" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with(**expected_hash) expect { d.foo(expected_hash) }.to fail_with( "# received :foo with unexpected arguments\n" \ " expected: ({:baz=>:quz, :foo=>:bar}) (keyword arguments)\n" \ " got: ({:baz=>:quz, :foo=>:bar}) (options hash)" ) end end RUBY eval <<-'RUBY', nil, __FILE__, __LINE__ + 1 it "prints a diff when keyword argument were expected but got an option hash (literal)" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with(:positional, keyword: 1) expect { options = { keyword: 1 } d.foo(:positional, options) }.to fail_with( "# received :foo with unexpected arguments\n" \ " expected: (:positional, {:keyword=>1}) (keyword arguments)\n" \ " got: (:positional, {:keyword=>1}) (options hash)" ) end end RUBY eval <<-'RUBY', nil, __FILE__, __LINE__ + 1 it "prints a diff when the positional argument doesnt match" do with_unfulfilled_double do |d| input = Class.new expected_input = input.new() actual_input = input.new() expect(d).to receive(:foo).with(expected_input, one: 1) expect { options = { one: 1 } d.foo(actual_input, options) }.to fail_with( "# received :foo with unexpected arguments\n" \ " expected: (#{expected_input.inspect}, {:one=>1}) (keyword arguments)\n" \ " got: (#{actual_input.inspect}, {:one=>1}) (options hash)\n" \ "Diff:\n" \ "@@ -1 +1 @@\n" \ "-[#{expected_input.inspect}, {:one=>1}]\n" \ "+[#{actual_input.inspect}, {:one=>1}]\n" ) end end RUBY end end context 'with keyword arguments on partial doubles' do include_context "with isolated configuration" let(:d) { Class.new { def foo(a, b); end }.new } before(:example) do RSpec::Mocks.configuration.verify_partial_doubles = true allow(RSpec.configuration).to receive(:color_enabled?) { false } end after(:example) { reset d } if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash? eval <<-'RUBY', nil, __FILE__, __LINE__ + 1 it "prints a diff when keyword argument were expected but got an option hash (using splat)" do expect(d).to receive(:foo).with(:positional, **expected_hash) expect { d.foo(:positional, expected_hash) }.to fail_with( "#{d.inspect} received :foo with unexpected arguments\n" \ " expected: (:positional, {:baz=>:quz, :foo=>:bar}) (keyword arguments)\n" \ " got: (:positional, {:baz=>:quz, :foo=>:bar}) (options hash)" ) end RUBY eval <<-'RUBY', nil, __FILE__, __LINE__ + 1 it "prints a diff when keyword argument were expected but got an option hash (literal)" do expect(d).to receive(:foo).with(:positional, keyword: 1) expect { options = { keyword: 1 } d.foo(:positional, options) }.to fail_with( "#{d.inspect} received :foo with unexpected arguments\n" \ " expected: (:positional, {:keyword=>1}) (keyword arguments)\n" \ " got: (:positional, {:keyword=>1}) (options hash)" ) end RUBY eval <<-'RUBY', nil, __FILE__, __LINE__ + 1 it "prints a diff when the positional argument doesnt match" do input = Class.new expected_input = input.new() actual_input = input.new() expect(d).to receive(:foo).with(expected_input, one: 1) expect { options = { one: 1 } d.foo(actual_input, options) }.to fail_with( "#{d.inspect} received :foo with unexpected arguments\n" \ " expected: (#{expected_input.inspect}, {:one=>1}) (keyword arguments)\n" \ " got: (#{actual_input.inspect}, {:one=>1}) (options hash)\n" \ "Diff:\n" \ "@@ -1 +1 @@\n" \ "-[#{expected_input.inspect}, {:one=>1}]\n" \ "+[#{actual_input.inspect}, {:one=>1}]\n" ) end RUBY end end if RUBY_VERSION.to_f < 1.9 # Ruby 1.8 hashes are not ordered, but `#inspect` on a particular unchanged # hash instance should return consistent output. However, on Travis that does # not always seem to be true and we have no idea why. Somehow, the travis build # has occasionally failed due to the output ordering varying between `inspect` # calls to the same hash. This regex allows us to work around that. def hash_regex_inspect(hash) "\\{(#{hash.map { |key, value| "#{key.inspect}=>#{value.inspect}.*" }.join "|"}){#{hash.size}}\\}" end else def hash_regex_inspect(hash) Regexp.escape(hash.inspect) end end it "prints a diff with array args" do with_unfulfilled_double do |d| expect(d).to receive(:foo).with([:a, :b, :c]) expect { d.foo([]) }.to fail_with("# received :foo with unexpected arguments\n expected: ([:a, :b, :c])\n got: ([])\nDiff:\n@@ #{one_line_header} @@\n-[[:a, :b, :c]]\n+[[]]\n") end end context "that defines #description" do it "does not use the object's description for a non-matcher object that implements #description" do with_unfulfilled_double do |d| collab = double(:collab, :description => "This string") collab_inspect = collab.inspect expect(d).to receive(:foo).with(collab) expect { d.foo([]) }.to fail_with("# received :foo with unexpected arguments\n" \ " expected: (#{collab_inspect})\n" \ " got: ([])\nDiff:\n@@ #{one_line_header} @@\n-[#{collab_inspect}]\n+[[]]\n") end end end end context "with a matcher object" do context "that defines #description" do it "uses the object's description" do with_unfulfilled_double do |d| collab = fake_matcher(Object.new) collab_description = collab.description expect(d).to receive(:foo).with(collab) expect { d.foo([:a, :b]) }.to fail_with("# received :foo with unexpected arguments\n" \ " expected: (#{collab_description})\n" \ " got: ([:a, :b])\nDiff:\n@@ #{one_line_header} @@\n-[\"#{collab_description}\"]\n+[[:a, :b]]\n") end end end context "that does not define #description" do it "for a matcher object that does not implement #description" do with_unfulfilled_double do |d| collab = Class.new do def self.name "RSpec::Mocks::ArgumentMatchers::" end def inspect "#" end end.new expect(RSpec::Support.is_a_matcher?(collab)).to be true collab_inspect = collab.inspect collab_pp = PP.pp(collab, "".dup).strip expect(d).to receive(:foo).with(collab) expect { d.foo([:a, :b]) }.to fail_with("# received :foo with unexpected arguments\n" \ " expected: (#{collab_inspect})\n" \ " got: ([:a, :b])\nDiff:\n@@ #{one_line_header} @@\n-[#{collab_pp}]\n+[[:a, :b]]\n") end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/double_spec.rb000066400000000000000000000746271455767030500220530ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe Double do before(:each) { @double = double("test double") } after(:each) { reset @double } it "has method_missing as private" do expect(RSpec::Mocks::Double.private_instance_methods).to include_method(:method_missing) end it "does not respond_to? method_missing (because it's private)" do expect(RSpec::Mocks::Double.new).not_to respond_to(:method_missing) end it "uses 'Double' in failure messages" do dbl = double('name') expect { dbl.foo }.to raise_error(/# received/) end it "hides internals in its inspect representation" do m = double('cup') expect(m.inspect).to eq('#') end it 'does not blow up when resetting standard object methods' do dbl = double(:tainted? => true) expect(dbl.tainted?).to eq(true) expect { reset dbl }.not_to raise_error end it 'does not get string vs symbol messages confused' do dbl = double("foo" => 1) allow(dbl).to receive(:foo).and_return(2) expect(dbl.foo).to eq(2) expect { reset dbl }.not_to raise_error end it "generates the correct error when an unfulfilled expectation uses an unused double as a `with` argument" do expect { a = double('a') b = double('b') expect(a).to receive(:append).with(b) verify_all }.to fail end it 'allows string representation of methods in constructor' do dbl = double('foo' => 1) expect(dbl.foo).to eq(1) end it 'allows setter methods to be stubbed' do dbl = double('foo=' => 1) # Note the specified return value is thrown away. This is a Ruby semantics # thing. You cannot change the return value of assignment. expect(dbl.foo = "bar").to eq("bar") end it 'allows `class` to be stubbed even when `any_instance` has already been used' do # See https://github.com/rspec/rspec-mocks/issues/687 # The infinite recursion code path was only triggered when there were # active any instance recorders in the current example, so we make one here. allow_any_instance_of(Object).to receive(:bar).and_return(2) dbl = double(:foo => 1, :class => String) expect(dbl.foo).to eq(1) expect(dbl.class).to eq(String) end it 'allows `send` to be stubbed' do dbl = double allow(dbl).to receive(:send).and_return("received") expect(dbl.send(:some_msg)).to eq("received") end it "reports line number of expectation of unreceived message" do expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) expect { verify @double }.to fail { |e| # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) } end it "reports line number of expectation of unreceived message after a message expectation after similar stub" do allow(@double).to receive(:wont_happen) expected_error_line = __LINE__; expect(@double).to receive(:wont_happen).with("x", 3) expect { verify @double }.to fail { |e| # NOTE - this regexp ended w/ $, but jruby adds extra info at the end of the line expect(e.backtrace[0]).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) } end it "passes when not receiving message specified as not to be received" do expect(@double).not_to receive(:not_expected) verify @double end it "prevents confusing double-negative expressions involving `never`" do expect { expect(@double).not_to receive(:not_expected).never }.to raise_error(/trying to negate it again/) end it "warns when `and_return` is called on a negative expectation" do expect { expect(@double).not_to receive(:do_something).and_return(1) }.to raise_error(/not supported/) expect { expect(@double).not_to receive(:do_something).and_return(1) }.to raise_error(/not supported/) expect { expect(@double).to receive(:do_something).never.and_return(1) }.to raise_error(/not supported/) end it "passes when receiving message specified as not to be received with different args" do expect(@double).not_to receive(:message).with("unwanted text") expect(@double).to receive(:message).with("other text") @double.message "other text" verify @double end it "fails when receiving message specified as not to be received" do expect(@double).not_to receive(:not_expected) expect { @double.not_expected }.to raise_error( RSpec::Mocks::MockExpectationError, %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| ) end it "fails when receiving message specified as not to be received with args" do expect(@double).not_to receive(:not_expected).with("unexpected text") expect { @double.not_expected("unexpected text") }.to raise_error( RSpec::Mocks::MockExpectationError, %Q|(Double "test double").not_expected("unexpected text")\n expected: 0 times with arguments: ("unexpected text")\n received: 1 time with arguments: ("unexpected text")| ) end it "fails when array arguments do not match" do expect(@double).not_to receive(:not_expected).with(["do not want"]) expect { @double.not_expected(["do not want"]) }.to raise_error( RSpec::Mocks::MockExpectationError, %Q|(Double "test double").not_expected(["do not want"])\n expected: 0 times with arguments: (["do not want"])\n received: 1 time with arguments: (["do not want"])| ) end context "when specifying a message should not be received with specific args" do context "using `expect(...).not_to receive()`" do it 'passes when receiving the message with different args' do expect(@double).not_to receive(:not_expected).with("unexpected text") @double.not_expected "really unexpected text" verify @double end end context "using `expect(...).to receive().never`" do it 'passes when receiving the message with different args' do expect(@double).to receive(:not_expected).with("unexpected text").never @double.not_expected "really unexpected text" verify @double end end end it 'does not get confused when a negative expectation is used with a string and symbol message' do allow(@double).to receive(:foo) { 3 } expect(@double).not_to receive(:foo).with(1) expect(@double).not_to receive("foo").with(2) expect(@double.foo).to eq(3) verify @double end it 'does not get confused when a positive expectation is used with a string and symbol message' do expect(@double).to receive(:foo).with(1) expect(@double).to receive("foo").with(2) @double.foo(1) @double.foo(2) verify @double end it "allows parameter as return value" do expect(@double).to receive(:something).with("a", "b", "c").and_return("booh") expect(@double.something("a", "b", "c")).to eq "booh" verify @double end it "returns the previously stubbed value if no return value is set" do allow(@double).to receive(:something).with("a", "b", "c").and_return(:stubbed_value) expect(@double).to receive(:something).with("a", "b", "c") expect(@double.something("a", "b", "c")).to eq :stubbed_value verify @double end it "returns nil if no return value is set and there is no previously stubbed value" do expect(@double).to receive(:something).with("a", "b", "c") expect(@double.something("a", "b", "c")).to be_nil verify @double end it "raises exception if args don't match when method called" do expect(@double).to receive(:something).with("a", "b", "c").and_return("booh") expect { @double.something("a", "d", "c") }.to fail_with "# received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end describe "even when a similar expectation with different arguments exist" do it "raises exception if args don't match when method called, correctly reporting the offending arguments" do expect(@double).to receive(:something).with("a", "b", "c").once expect(@double).to receive(:something).with("z", "x", "c").once expect { @double.something("a", "b", "c") @double.something("z", "x", "g") }.to fail_with "# received :something with unexpected arguments\n expected: (\"z\", \"x\", \"c\")\n got: (\"z\", \"x\", \"g\")" end end it "raises exception if args don't match when method called even when the method is stubbed" do allow(@double).to receive(:something) expect(@double).to receive(:something).with("a", "b", "c") expect { @double.something("a", "d", "c") verify @double }.to fail_with "# received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end it "raises exception if args don't match when method called even when using null_object" do @double = double("test double").as_null_object expect(@double).to receive(:something).with("a", "b", "c") expect { @double.something("a", "d", "c") verify @double }.to fail_with "# received :something with unexpected arguments\n expected: (\"a\", \"b\", \"c\")\n got: (\"a\", \"d\", \"c\")" end describe 'with a method that has a default argument' do it "raises an exception if the arguments don't match when the method is called, correctly reporting the offending arguments" do def @double.method_with_default_argument(_={}); end expect(@double).to receive(:method_with_default_argument).with({}) expect { @double.method_with_default_argument(nil) verify @double }.to fail_with a_string_starting_with("# received :method_with_default_argument with unexpected arguments\n expected: ({})\n got: (nil)") end end it "fails if unexpected method called" do expect { @double.something("a", "b", "c") }.to fail_with "# received unexpected message :something with (\"a\", \"b\", \"c\")" end it "uses block for expectation if provided" do expect(@double).to receive(:something) do | a, b | expect(a).to eq "a" expect(b).to eq "b" "booh" end expect(@double.something("a", "b")).to eq "booh" verify @double end it "fails if expectation block fails" do expect(@double).to receive(:something) do |bool| expect(bool).to be_truthy end expect { @double.something false }.to raise_error(RSpec::Expectations::ExpectationNotMetError) end it "is wrappable in an array" do with_isolated_stderr do expect(Array(@double)).to eq([@double]) end end it "is wrappable in an array when a null object" do with_isolated_stderr do expect(Array(@double.as_null_object)).to eq [@double] end end it "responds to to_ary as a null object" do expect(@double.as_null_object.to_ary).to eq nil end it "responds to to_a as a null object" do if RUBY_VERSION.to_f > 1.8 expect(@double.as_null_object.to_a).to eq nil else with_isolated_stderr do expect(@double.as_null_object.to_a).to eq [@double] end end end it "passes proc to expectation block without an argument" do expect(@double).to receive(:foo) { |&block| expect(block.call).to eq(:bar) } @double.foo { :bar } end it "passes proc to expectation block with an argument" do expect(@double).to receive(:foo) { |_, &block| expect(block.call).to eq(:bar) } @double.foo(:arg) { :bar } end it "passes proc to stub block without an argument" do allow(@double).to receive(:foo) { |&block| expect(block.call).to eq(:bar) } @double.foo { :bar } end it "passes proc to stub block with an argument" do allow(@double).to receive(:foo) { |_, &block| expect(block.call).to eq(:bar) } @double.foo(:arg) { :bar } end it "fails right away when method defined as never is received" do expect(@double).to receive(:not_expected).never expect { @double.not_expected }. to fail_with %Q|(Double "test double").not_expected(no args)\n expected: 0 times with any arguments\n received: 1 time| end it "raises RuntimeError by default" do expect(@double).to receive(:something).and_raise expect { @double.something }.to raise_error(RuntimeError) end it "raises RuntimeError with a message by default" do expect(@double).to receive(:something).and_raise("error message") expect { @double.something }.to raise_error(RuntimeError, "error message") end it "raises an exception of a given type without an error message" do expect(@double).to receive(:something).and_raise(StandardError) expect { @double.something }.to raise_error(StandardError) end it "raises an exception of a given type with a message" do expect(@double).to receive(:something).and_raise(RuntimeError, "error message") expect { @double.something }.to raise_error(RuntimeError, "error message") end it "raises a given instance of an exception" do expect(@double).to receive(:something).and_raise(RuntimeError.new("error message")) expect { @double.something }.to raise_error(RuntimeError, "error message") end class OutOfGas < StandardError attr_reader :amount, :units def initialize(amount, units) @amount = amount @units = units end end it "raises a given instance of an exception with arguments other than the standard 'message'" do expect(@double).to receive(:something).and_raise(OutOfGas.new(2, :oz)) expect { @double.something }.to raise_error(OutOfGas) { |e| expect(e.amount).to eq 2 expect(e.units).to eq :oz } end it "does not raise when told to if args dont match" do expect(@double).to receive(:something).with(2).and_raise(RuntimeError) expect { @double.something 1 }.to fail end it "throws when told to" do expect(@double).to receive(:something).and_throw(:blech) expect { @double.something }.to throw_symbol(:blech) end it "ignores args on any args" do expect(@double).to receive(:something).at_least(:once).with(any_args) @double.something @double.something 1 @double.something "a", 2 @double.something [], {}, "joe", 7 verify @double end it "fails on no args if any args received" do expect(@double).to receive(:something).with(no_args) expect { @double.something 1 }.to fail_with "# received :something with unexpected arguments\n expected: (no args)\n got: (1)" end it "fails when args are expected but none are received" do expect(@double).to receive(:something).with(1) expect { @double.something }.to fail_with "# received :something with unexpected arguments\n expected: (1)\n got: (no args)" end it "returns value from block by default" do allow(@double).to receive(:method_that_yields).and_yield value = @double.method_that_yields { :returned_obj } expect(value).to eq :returned_obj verify @double end it "yields 0 args to blocks that take a variable number of arguments" do expect(@double).to receive(:yield_back).with(no_args).once.and_yield a = nil @double.yield_back { |*x| a = x } expect(a).to eq [] verify @double end it "yields 0 args multiple times to blocks that take a variable number of arguments" do expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield. and_yield b = [] @double.yield_back { |*a| b << a } expect(b).to eq [[], []] verify @double end it "yields one arg to blocks that take a variable number of arguments" do expect(@double).to receive(:yield_back).with(no_args).once.and_yield(99) a = nil @double.yield_back { |*x| a = x } expect(a).to eq [99] verify @double end it "yields one arg 3 times consecutively to blocks that take a variable number of arguments" do expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield(99). and_yield(43). and_yield("something fruity") b = [] @double.yield_back { |*a| b << a } expect(b).to eq [[99], [43], ["something fruity"]] verify @double end it "yields many args to blocks that take a variable number of arguments" do expect(@double).to receive(:yield_back).with(no_args).once.and_yield(99, 27, "go") a = nil @double.yield_back { |*x| a = x } expect(a).to eq [99, 27, "go"] verify @double end it "yields many args 3 times consecutively to blocks that take a variable number of arguments" do expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield(99, :green, "go"). and_yield("wait", :amber). and_yield("stop", 12, :red) b = [] @double.yield_back { |*a| b << a } expect(b).to eq [[99, :green, "go"], ["wait", :amber], ["stop", 12, :red]] verify @double end it "yields single value" do expect(@double).to receive(:yield_back).with(no_args).once.and_yield(99) a = nil @double.yield_back { |x| a = x } expect(a).to eq 99 verify @double end it "yields single value 3 times consecutively" do expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield(99). and_yield(43). and_yield("something fruity") b = [] @double.yield_back { |a| b << a } expect(b).to eq [99, 43, "something fruity"] verify @double end it "yields two values" do expect(@double).to receive(:yield_back).with(no_args).once.and_yield('wha', 'zup') a, b = nil @double.yield_back { |x, y| a = x; b = y } expect(a).to eq 'wha' expect(b).to eq 'zup' verify @double end it "yields two values 3 times consecutively" do expect(@double).to receive(:yield_back).once.with(no_args).once.and_yield('wha', 'zup'). and_yield('not', 'down'). and_yield(14, 65) c = [] @double.yield_back { |a, b| c << [a, b] } expect(c).to eq [%w[wha zup], %w[not down], [14, 65]] verify @double end it "fails when calling yielding method with wrong arity" do expect(@double).to receive(:yield_back).with(no_args).once.and_yield('wha', 'zup') expect { @double.yield_back { |_| } }.to fail_with "# yielded |\"wha\", \"zup\"| to block with arity of 1" end if kw_args_supported? it 'fails when calling yielding method with invalid kw args' do expect(@double).to receive(:yield_back).and_yield(:x => 1, :y => 2) expect { eval("@double.yield_back { |x: 1| }") }.to fail_with '# yielded |{:x=>1, :y=>2}| to block with optional keyword args (:x)' end end it "fails when calling yielding method consecutively with wrong arity" do expect(@double).to receive(:yield_back).once.with(no_args).and_yield('wha', 'zup'). and_yield('down'). and_yield(14, 65) expect { c = [] @double.yield_back { |a, b| c << [a, b] } }.to fail_with "# yielded |\"down\"| to block with arity of 2" end it "fails when calling yielding method without block" do expect(@double).to receive(:yield_back).with(no_args).once.and_yield('wha', 'zup') expect { @double.yield_back }.to fail_with "# asked to yield |[\"wha\", \"zup\"]| but no block was passed" end it "is able to double send" do expect(@double).to receive(:send).with(any_args) @double.send 'hi' verify @double end it "is able to raise from method calling yielding double" do expect(@double).to receive(:yield_me).and_yield 44 expect { @double.yield_me do |_| raise "Bang" end }.to raise_error(StandardError, "Bang") verify @double end it "clears expectations after verify" do expect(@double).to receive(:foobar) @double.foobar verify @double expect { @double.foobar }.to fail_with %q|# received unexpected message :foobar with (no args)| end it "restores objects to their original state on rspec_reset" do dbl = double("this is a double") expect(dbl).to receive(:blah) reset dbl verify dbl # should throw if reset didn't work end it "temporarily replaces a method stub on a double" do allow(@double).to receive(:msg).and_return(:stub_value) expect(@double).to receive(:msg).with(:arg).and_return(:double_value) expect(@double.msg(:arg)).to equal(:double_value) expect(@double.msg).to equal(:stub_value) expect(@double.msg).to equal(:stub_value) verify @double end it "does not require a different signature to replace a method stub" do allow(@double).to receive(:msg).and_return(:stub_value) expect(@double).to receive(:msg).and_return(:double_value) expect(@double.msg(:arg)).to equal(:double_value) expect(@double.msg).to equal(:stub_value) expect(@double.msg).to equal(:stub_value) verify @double end it "raises an error when a previously stubbed method has a negative expectation" do allow(@double).to receive(:msg).and_return(:stub_value) expect(@double).not_to receive(:msg) expect { @double.msg(:arg) }.to fail end it "temporarily replaces a method stub on a non-double" do non_double = Object.new allow(non_double).to receive(:msg).and_return(:stub_value) expect(non_double).to receive(:msg).with(:arg).and_return(:double_value) expect(non_double.msg(:arg)).to equal(:double_value) expect(non_double.msg).to equal(:stub_value) expect(non_double.msg).to equal(:stub_value) verify non_double end it "returns the stubbed value when no new value specified" do allow(@double).to receive(:msg).and_return(:stub_value) expect(@double).to receive(:msg) expect(@double.msg).to equal(:stub_value) verify @double end it "returns the stubbed value when stubbed with args and no new value specified" do allow(@double).to receive(:msg).with(:arg).and_return(:stub_value) expect(@double).to receive(:msg).with(:arg) expect(@double.msg(:arg)).to equal(:stub_value) verify @double end it "does not mess with the stub's yielded values when also doubleed" do allow(@double).to receive(:yield_back).and_yield(:stub_value) expect(@double).to receive(:yield_back).and_yield(:double_value) @double.yield_back { |v| expect(v).to eq :double_value } @double.yield_back { |v| expect(v).to eq :stub_value } verify @double end it "can yield multiple times when told to do so" do allow(@double).to receive(:foo).and_yield(:stub_value) expect(@double).to receive(:foo).and_yield(:first_yield).and_yield(:second_yield) expect { |b| @double.foo(&b) }.to yield_successive_args(:first_yield, :second_yield) expect { |b| @double.foo(&b) }.to yield_with_args(:stub_value) verify @double end it "assigns stub return values" do dbl = RSpec::Mocks::Double.new('name', :message => :response) expect(dbl.message).to eq :response end describe "a double message receiving a block" do before(:each) do @double = double("double") @calls = 0 end def add_call @calls += 1 end it "supports a block passed to `receive` for `expect`" do expect(@double).to receive(:foo) { add_call } @double.foo expect(@calls).to eq 1 end it "supports a block passed to `receive` for `expect` after a similar stub" do allow(@double).to receive(:foo).and_return(:bar) expect(@double).to receive(:foo) { add_call } @double.foo expect(@calls).to eq 1 end it "calls the block after #once" do expect(@double).to receive(:foo).once { add_call } @double.foo expect(@calls).to eq 1 end it "calls the block after #twice" do expect(@double).to receive(:foo).twice { add_call } @double.foo @double.foo expect(@calls).to eq 2 end it "calls the block after #times" do expect(@double).to receive(:foo).exactly(10).times { add_call } (1..10).each { @double.foo } expect(@calls).to eq 10 end it "calls the block after #ordered" do expect(@double).to receive(:foo).ordered { add_call } expect(@double).to receive(:bar).ordered { add_call } @double.foo @double.bar expect(@calls).to eq 2 end end describe 'string representation generated by #to_s' do it 'does not contain < because that might lead to invalid HTML in some situations' do dbl = double("Dog") valid_html_str = "#{dbl}" expect(valid_html_str).not_to include('<') end end describe "#to_str", :unless => RUBY_VERSION == '1.9.2' do it "should not respond to #to_str to avoid being coerced to strings by the runtime" do dbl = double("Foo") expect { dbl.to_str }.to raise_error( RSpec::Mocks::MockExpectationError, '# received unexpected message :to_str with (no args)') end end describe "double created with no name" do it "does not use a name in a failure message" do dbl = double expect { dbl.foo }.to raise_error.with_message(a_string_including("# received")) end it "does respond to initially stubbed methods" do dbl = double(:foo => "woo", :bar => "car") expect(dbl.foo).to eq "woo" expect(dbl.bar).to eq "car" end end describe "==" do it "sends '== self' to the comparison object" do first = double('first') second = double('second') expect(first).to receive(:==).with(second) second == first end end describe "with" do before { @double = double('double') } context "with args" do context "with matching args" do it "passes" do expect(@double).to receive(:foo).with('bar') @double.foo('bar') end end context "with non-matching args" do it "fails" do expect(@double).to receive(:foo).with('bar') expect do @double.foo('baz') end.to fail reset @double end end context "with non-matching doubles" do it "fails" do d1 = double('1') d2 = double('2') expect(@double).to receive(:foo).with(d1) expect do @double.foo(d2) end.to fail reset @double end end context "with non-matching doubles as_null_object" do it "fails" do d1 = double('1').as_null_object d2 = double('2').as_null_object expect(@double).to receive(:foo).with(d1) expect do @double.foo(d2) end.to fail reset @double end end end context "with a block" do context "with matching args" do it "returns the result of the block" do expect(@double).to receive(:foo).with('bar') { 'baz' } expect(@double.foo('bar')).to eq('baz') end end context "with non-matching args" do it "fails" do expect(@double).to receive(:foo).with('bar') { 'baz' } expect do expect(@double.foo('wrong')).to eq('baz') end.to raise_error(/received :foo with unexpected arguments/) reset @double end end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/error_generator_spec.rb000066400000000000000000000054511455767030500237650ustar00rootroot00000000000000require "spec_helper" module RSpec module Mocks RSpec.describe ErrorGenerator do context "when inserting a backtrace line" do def has_java_frames? yield rescue RSpec::Mocks::MockExpectationError => e e.backtrace.grep(/\.java:/).any? else raise "got no exception" end it "produces stacktraces that match how `raise` produces stacktraces (on JRuby `caller` and `raise` can differ about the presence of java frames)" do raise_has_java_frames = has_java_frames? { raise RSpec::Mocks::MockExpectationError } eg_has_java_frames = has_java_frames? do ErrorGenerator.new.send(:__raise, "message", "foo.rb:1") end expect(raise_has_java_frames).to eq eg_has_java_frames end end def unexpected_failure_message_for(object_description) /received unexpected message :bees with \(#{object_description}\)/ end describe "formatting arguments" do it 'formats time objects with increased precision' do time = Time.utc(1969, 12, 31, 19, 01, 40, 101) expected_output = "1969-12-31 19:01:40.000101" o = double(:double) expect { o.bees(time) }.to fail_including(expected_output) end context "on non-matcher objects that define #description" do it "does not use the object's description" do o = double(:double, :description => "Friends") expect { o.bees(o) }.to fail_with(unexpected_failure_message_for(o.inspect)) end end context "on matcher objects" do context "that define description" do it "uses the object's description" do d = double(:double) o = fake_matcher(Object.new) expect { d.bees(o) }.to raise_error(unexpected_failure_message_for(o.description)) end end context "that do not define description" do it "does not use the object's description" do d = double(:double) o = Class.new do def self.name "RSpec::Mocks::ArgumentMatchers::" end end.new expect(RSpec::Support.is_a_matcher?(o)).to be true expect { d.bees(o) }.to fail_with(unexpected_failure_message_for(o.inspect)) end end context "on default method stub" do it "error message display starts in new line" do d = double(:double) allow(d).to receive(:foo).with({}) expect { d.foo([]) }.to fail_with(/\nDiff/) end end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/example_methods_spec.rb000066400000000000000000000023641455767030500237440ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe ExampleMethods do it 'does not define private helper methods since it gets included into a ' \ 'namespace where users define methods and could inadvertently overwrite ' \ 'them' do expect(ExampleMethods.private_instance_methods).to eq([]) end def test_extend_on_new_object(*to_extend, &block) host = Object.new to_extend.each { |mod| host.extend mod } host.instance_eval do dbl = double expect(dbl).to receive(:foo).at_least(:once).and_return(1) dbl.foo instance_exec(dbl, &block) if block end end it 'works properly when extended onto an object' do test_extend_on_new_object ExampleMethods end it 'works properly when extended onto an object that has previous extended `RSpec::Matchers`' do test_extend_on_new_object RSpec::Matchers, ExampleMethods do |dbl| expect(dbl.foo).to eq(1) end end it 'works properly when extended onto an object that later extends `RSpec::Matchers`' do test_extend_on_new_object ExampleMethods, RSpec::Matchers do |dbl| expect(dbl.foo).to eq(1) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/expiration_spec.rb000066400000000000000000000047561455767030500227570ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "After a test double has been torn down" do RSpec.shared_examples_for "expiration" do before do expect(dbl).to receive(:foo).at_least(:once) allow(dbl).to receive(:bar) dbl.foo RSpec::Mocks.verify RSpec::Mocks.teardown RSpec::Mocks.setup end it 'disallows previously mocked methods' do expect { dbl.foo }.to raise_error(ExpiredTestDoubleError) end it 'disallows previously stubbed methods' do expect { dbl.bar }.to raise_error(ExpiredTestDoubleError) end it 'disallows stubbing new methods (with receive)' do expect { allow(dbl).to receive(:bazz) }.to raise_error(ExpiredTestDoubleError) end it 'disallows stubbing new methods (with receive_messages)' do expect { allow(dbl).to receive_messages(:bazz => 3) }.to raise_error(ExpiredTestDoubleError) end it 'disallows stubbing new message chains' do expect { allow(dbl).to receive_message_chain(:bazz, :bam, :goo) }.to raise_error(ExpiredTestDoubleError) end it 'disallows mocking new methods' do expect { expect(dbl).to receive(:bazz) }.to raise_error(ExpiredTestDoubleError) end it 'disallows being turned into a null object' do expect { dbl.as_null_object }.to raise_error(ExpiredTestDoubleError) end it 'disallows being checked for nullness' do expect { dbl.null_object? }.to raise_error(ExpiredTestDoubleError) end end context "for a plain double" do let(:dbl) { double } include_examples "expiration" end class ExpiredInstanceInterface def foo; end def bar; end def bazz; end end class ExpiredClassInterface def self.foo; end def self.bar; end def self.bazz; end end context "for an instance_double" do let(:dbl) { instance_double(ExpiredInstanceInterface) } include_examples "expiration" end context "for a class_double" do let(:dbl) { class_double(ExpiredClassInterface) } include_examples "expiration" end context "for an object_double" do let(:dbl) { object_double(ExpiredInstanceInterface.new) } include_examples "expiration" end end end end rspec-mocks-3.13.0/spec/rspec/mocks/failure_notification_spec.rb000066400000000000000000000050161455767030500247600ustar00rootroot00000000000000RSpec.describe "Failure notification" do def capture_errors(&block) errors = [] RSpec::Support.with_failure_notifier(Proc.new { |e, _opts| errors << e }, &block) errors end it "uses the rspec-support notifier to support `aggregate_failures`" do dbl = double("Foo") expect(capture_errors { dbl.some_unallowed_method }).to match [an_object_having_attributes( :message => a_string_including(dbl.inspect, "some_unallowed_method") )] end it "includes the line of future expectation in the notification for an unreceived message" do dbl = double("Foo") expect(dbl).to receive(:wont_happen); expected_from_line = __LINE__ error = capture_errors { verify dbl }.first expect(error.backtrace.first).to match(/#{File.basename(__FILE__)}:#{expected_from_line}/) end it "does not allow a double to miscount the number of times a message was received when a failure is notified in an alternate way" do dbl = double("Foo") expect(dbl).not_to receive(:bar) capture_errors { dbl.bar } expect { verify dbl }.to fail_including("expected: 0 times", "received: 1 time") end context "when using `aggregate_failures`" do specify 'spy failures for unreceived messages are reported correctly' do expect { aggregate_failures do expect(spy).to have_received(:foo) end }.to raise_error(RSpec::Expectations::ExpectationNotMetError) do |e| expect(e).not_to be_a(RSpec::Expectations::MultipleExpectationsNotMetError) expect(e.message).to include("expected: 1 time", "received: 0 times") end end specify 'spy failures for messages received with unexpected args are reported correctly' do expect { aggregate_failures do the_spy = spy the_spy.foo(1) expect(the_spy).to have_received(:foo).with(2) end }.to raise_error(RSpec::Expectations::ExpectationNotMetError) do |e| expect(e).not_to be_a(RSpec::Expectations::MultipleExpectationsNotMetError) expect(e.message).to include("expected: (2)", "got: (1)") end end specify "failing negative expectations are only notified once" do expect { aggregate_failures do dbl = double expect(dbl).not_to receive(:foo) expect(dbl).not_to receive(:bar) dbl.foo dbl.bar verify_all end }.to raise_error(RSpec::Expectations::MultipleExpectationsNotMetError) do |e| expect(e.failures.count).to eq(2) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/formatting_spec.rb000066400000000000000000000067021455767030500227400ustar00rootroot00000000000000require 'rspec/matchers/fail_matchers' require 'support/doubled_classes' RSpec.describe "Test doubles format well in failure messages" do include RSpec::Matchers::FailMatchers include RSpec::Support::Spec::DiffHelpers RSpec::Matchers.define :format_in_failures_as do |expected| match do |dbl| values_match?(expected, actual_formatting(dbl)) end def actual_formatting(double) expect(1).to eq(double) rescue RSpec::Expectations::ExpectationNotMetError => e e.message[/expected: (.*)$/, 1] else raise "Did not fail as expected" end end describe "`double`" do context "with a name" do specify '#' do expect(double "Book").to format_in_failures_as('#') end it 'formats the name as a symbol if that was how it was provided' do expect(double :book).to format_in_failures_as('#') end end context "without a name" do specify '#' do expect(double).to format_in_failures_as('#') end end it 'avoids sending `instance_variable_get` to the double as it may be stubbed' do dbl = double("Book") expect(dbl).not_to receive(:instance_variable_get) expect(dbl).to format_in_failures_as('#') end end describe "`instance_double(SomeClass)`" do context "with a name" do specify '#' do expect(instance_double(LoadedClass, "Book")).to format_in_failures_as('#') end end context "without a name" do specify '#' do expect(instance_double(LoadedClass)).to format_in_failures_as('#') end end it 'avoids sending `instance_variable_get` to the double as it may be stubbed' do dbl = instance_double(LoadedClass, "Book") expect(dbl).not_to receive(:instance_variable_get) expect(dbl).to format_in_failures_as('#') end end describe "`class_double(SomeClass)`" do context "with a name" do specify '#' do expect(class_double(LoadedClass, "Book")).to format_in_failures_as('#') end end context "without a name" do specify '#' do expect(class_double(LoadedClass)).to format_in_failures_as('#') end end end describe "`object_double([])`" do context "with a name" do specify '#' do expect(object_double([], "Name")).to format_in_failures_as('#') end end context "without a name" do specify '#' do expect(object_double([])).to format_in_failures_as('#') end end end it 'formats the doubles when they appear in data structures and diffs' do allow(RSpec::Expectations.configuration).to receive(:color?).and_return(false) foo = double("Foo") bar = double("Bar") expect { expect([foo]).to include(bar) }.to fail_with(<<-EOS.gsub(/^\s+\|/, '')) |expected [#] to include # |Diff: |@@ #{one_line_header} @@ |-[#] |+[#] EOS end end rspec-mocks-3.13.0/spec/rspec/mocks/hash_excluding_matcher_spec.rb000066400000000000000000000045421455767030500252560ustar00rootroot00000000000000module RSpec module Mocks module ArgumentMatchers RSpec.describe HashExcludingMatcher do it "describes itself properly" do expect(HashExcludingMatcher.new(:a => 5).description).to eq "hash_not_including(:a=>5)" end describe "passing" do it "matches a hash without the specified key" do expect(hash_not_including(:c)).to be === {:a => 1, :b => 2} end it "matches a hash with the specified key, but different value" do expect(hash_not_including(:b => 3)).to be === {:a => 1, :b => 2} end it "matches a hash without the specified key, given as anything()" do expect(hash_not_including(:c => anything)).to be === {:a => 1, :b => 2} end it "matches an empty hash" do expect(hash_not_including(:a)).to be === {} end it "matches a hash without any of the specified keys" do expect(hash_not_including(:a, :b, :c)).to be === { :d => 7 } end it "matches against classes inheriting from Hash" do expect(hash_not_including(Class.new(Hash)[:c, 1])).not_to be === {:c => 1} end end describe "failing" do it "does not match a non-hash" do expect(hash_not_including(:a => 1)).not_to be === 1 end it "does not match a hash with a specified key" do expect(hash_not_including(:b)).not_to be === { :b => 2 } end it "does not match a hash with the specified key/value pair" do expect(hash_not_including(:b => 2)).not_to be === { :a => 1, :b => 2 } end it "does not match a hash with the specified key" do expect(hash_not_including(:a, :b => 3)).not_to be === { :a => 1, :b => 2 } end it "does not match a hash with one of the specified keys" do expect(hash_not_including(:a, :b)).not_to be === { :b => 2 } end it "does not match a hash with some of the specified keys" do expect(hash_not_including(:a, :b, :c)).not_to be === { :a => 1, :b => 2 } end it "does not match a hash with one key/value pair included" do expect(hash_not_including(:a, :b, :c, :d => 7)).not_to be === { :d => 7 } end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/hash_including_matcher_spec.rb000066400000000000000000000071771455767030500252570ustar00rootroot00000000000000module RSpec module Mocks module ArgumentMatchers RSpec.describe HashIncludingMatcher do it "describes itself properly" do expect(HashIncludingMatcher.new(:a => 1).description).to eq "hash_including(:a=>1)" end it "describes passed matchers" do description = hash_including(:foo => fake_matcher(Object.new)).description expect(description).to include(MatcherHelpers.fake_matcher_description) end describe "passing" do it "matches the same hash" do expect(hash_including(:a => 1)).to be === {:a => 1} end it "matches a hash with extra stuff" do expect(hash_including(:a => 1)).to be === {:a => 1, :b => 2} end it "matches against classes inheriting from Hash" do expect(hash_including(Class.new(Hash)[:a, 1])).to be === {:a => 1} end describe "when matching against other matchers" do it "matches an int against anything()" do expect(hash_including(:a => anything, :b => 2)).to be === {:a => 1, :b => 2} end it "matches a string against anything()" do expect(hash_including(:a => anything, :b => 2)).to be === {:a => "1", :b => 2} end it 'can match against arbitrary objects that implement #===' do expect(hash_including(:a => /foo/)).to be === { :a => "foobar" } end end describe "when passed only keys or keys mixed with key/value pairs" do it "matches if the key is present" do expect(hash_including(:a)).to be === {:a => 1, :b => 2} end it "matches if more keys are present" do expect(hash_including(:a, :b)).to be === {:a => 1, :b => 2, :c => 3} end it "matches a string against a given key" do expect(hash_including(:a)).to be === {:a => "1", :b => 2} end it "matches if passed one key and one key/value pair" do expect(hash_including(:a, :b => 2)).to be === {:a => 1, :b => 2} end it "matches if passed many keys and one key/value pair" do expect(hash_including(:a, :b, :c => 3)).to be === {:a => 1, :b => 2, :c => 3, :d => 4} end it "matches if passed many keys and many key/value pairs" do expect(hash_including(:a, :b, :c => 3, :e => 5)).to be === {:a => 1, :b => 2, :c => 3, :d => 4, :e => 5} end end end describe "failing" do it "does not match a non-hash" do expect(hash_including(:a => 1)).not_to be === 1 end it "does not match a hash with a missing key" do expect(hash_including(:a => 1)).not_to be === { :b => 2 } end it "does not match a hash with a missing key" do expect(hash_including(:a)).not_to be === { :b => 2 } end it "does not match an empty hash with a given key" do expect(hash_including(:a)).not_to be === {} end it "does not match a hash with a missing key when one pair is matching" do expect(hash_including(:a, :b => 2)).not_to be === { :b => 2 } end it "does not match a hash with an incorrect value" do expect(hash_including(:a => 1, :b => 2)).not_to be === { :a => 1, :b => 3 } end it "does not match when values are nil but keys are different" do expect(hash_including(:a => nil)).not_to be === { :b => nil } end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/instance_method_stasher_spec.rb000066400000000000000000000045701455767030500254640ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe InstanceMethodStasher do class ExampleClass def hello :hello_defined_on_class end end def singleton_class_for(obj) class << obj; self; end end def stasher_for(obj, method_name) InstanceMethodStasher.new(obj, method_name) end it "stashes the current implementation of an instance method so it can be temporarily replaced" do obj = Object.new def obj.hello; :hello_defined_on_singleton_class; end; stashed_method = stasher_for(obj, :hello) stashed_method.stash with_isolated_stderr { def obj.hello; :overridden_hello; end } expect(obj.hello).to eql :overridden_hello stashed_method.restore expect(obj.hello).to eql :hello_defined_on_singleton_class end it "stashes private instance methods" do obj = Object.new def obj.hello; :hello_defined_on_singleton_class; end; singleton_class_for(obj).__send__(:private, :hello) stashed_method = stasher_for(obj, :hello) stashed_method.stash with_isolated_stderr { def obj.hello; :overridden_hello; end } stashed_method.restore expect(obj.send(:hello)).to eql :hello_defined_on_singleton_class end it "only stashes methods directly defined on the given class, not its ancestors" do obj = ExampleClass.new stashed_method = stasher_for(obj, :hello) stashed_method.stash def obj.hello; :overridden_hello; end; expect(obj.hello).to eql :overridden_hello stashed_method.restore expect(obj.hello).to eql :overridden_hello end it "does not unnecessarily create obfuscated aliased methods", :if => (RUBY_VERSION.to_f > 1.8) do obj = Object.new def obj.hello; :hello_defined_on_singleton_class; end; stashed_method = stasher_for(obj, :hello) stashed_method.stash expect(obj.methods.grep(/rspec/)).to eq([]) end it "undefines the original method", :if => (RUBY_VERSION.to_f > 1.8) do obj = Object.new def obj.hello; :hello_defined_on_singleton_class; end; stashed_method = stasher_for(obj, :hello) stashed_method.stash expect(obj.methods).not_to include(:hello) expect(obj).not_to respond_to(:hello) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/marshal_extension_spec.rb000066400000000000000000000042211455767030500243030ustar00rootroot00000000000000RSpec.describe Marshal, 'extensions' do # An object that raises when code attempts to dup it. # # Because we manipulate the internals of RSpec::Mocks.space below, we need # an object that simply blows up when #dup is called without using any # partial mocking or stubbing from rspec-mocks itself. class UndupableObject def dup raise NotImplementedError end end describe '#dump' do context 'when rspec-mocks has been fully initialized' do include_context "with monkey-patched marshal" it 'duplicates objects with stubbed or mocked implementations before serialization' do obj = double(:foo => "bar") serialized = Marshal.dump(obj) expect(Marshal.load(serialized)).to be_an(obj.class) end it 'does not duplicate other objects before serialization' do obj = UndupableObject.new serialized = Marshal.dump(obj) expect(Marshal.load(serialized)).to be_an(UndupableObject) end it 'does not duplicate nil before serialization' do serialized = Marshal.dump(nil) expect(Marshal.load(serialized)).to be_nil end specify 'applying and unapplying patch is idempotent' do obj = double(:foo => "bar") config = RSpec::Mocks.configuration config.patch_marshal_to_support_partial_doubles = true config.patch_marshal_to_support_partial_doubles = true serialized = Marshal.dump(obj) expect(Marshal.load(serialized)).to be_an(obj.class) config.patch_marshal_to_support_partial_doubles = false config.patch_marshal_to_support_partial_doubles = false expect { Marshal.dump(obj) }.to raise_error(TypeError) end end context 'outside the per-test lifecycle' do def outside_per_test_lifecycle RSpec::Mocks.teardown yield ensure RSpec::Mocks.setup end it 'does not duplicate the object before serialization' do obj = UndupableObject.new outside_per_test_lifecycle do serialized = Marshal.dump(obj) expect(Marshal.load(serialized)).to be_an(UndupableObject) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/matchers/000077500000000000000000000000001455767030500210305ustar00rootroot00000000000000rspec-mocks-3.13.0/spec/rspec/mocks/matchers/have_received_spec.rb000066400000000000000000000674301455767030500251720ustar00rootroot00000000000000module RSpec module Mocks # This shared example group is highly unusual as it is used to test how # `have_received` works in two situations: # # * With rspec-mocks as a standalone library. # * Together with rspec-expectations. # # To simulate the former, we've had to hack things a bit. Special care must be taken: # # * Only define examples with `it`, (not `fit`, `xit`, `specify`, etc). We redefine # `it` below to make it support our needs here but that definition isn't applied to # the other forms. # * All normal expectations must use `_expect`, not `expect`. Expectations # for `have_received` should use `expect`. RSpec.shared_examples_for Matchers::HaveReceived do # Make rspec-expectations' `expect` available via an alias so that when # this group is included below in a context that simulates rspec-expectations # not being loaded by using rspec-mocks' `expect` instead of rspec-expectations' # `expect`, we still have a way to use the expectations one for normal expectations. # In this group, all normal expectations should use `_expect` instead of `expect`. alias _expect expect describe "expect(...).to have_received" do it 'passes when the double has received the given message' do dbl = double_with_met_expectation(:expected_method) expect(dbl).to have_received(:expected_method) end it 'passes when a null object has received the given message' do dbl = null_object_with_met_expectation(:expected_method) expect(dbl).to have_received(:expected_method) end it 'fails when the double has not received the given message' do dbl = double_with_unmet_expectation(:expected_method) _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/expected: 1 time/) end it "notifies failures via rspec-support's failure notification system" do dbl = double_with_unmet_expectation(:expected_method) captured = nil RSpec::Support.with_failure_notifier(Proc.new { |e, _opt| captured = e }) do expect(dbl).to have_received(:expected_method) end _expect(captured.message).to include("expected: 1 time") end it 'fails when a null object has not received the given message' do dbl = double.as_null_object _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/expected: 1 time/) end it 'fails when the method has not been previously stubbed' do dbl = double _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/method has not been stubbed/) end it 'fails when the method has been mocked' do dbl = double expect(dbl).to receive(:expected_method) dbl.expected_method _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/method has been mocked instead of stubbed/) end it "takes a curly-bracket block and yields the arguments given to the stubbed method call" do dbl = double(:foo => nil) yielded = [] dbl.foo(:a, :b, :c) expect(dbl).to have_received(:foo) { |*args| yielded << args } _expect(yielded).to include([:a, :b, :c]) end it "takes a do-end block and yields the arguments given to the stubbed method call" do dbl = double(:foo => nil) yielded = [] dbl.foo(:a, :b, :c) expect(dbl).to have_received(:foo) do |*args| yielded << args end _expect(yielded).to include([:a, :b, :c]) end it "passes if expectations against the yielded arguments pass" do dbl = double(:foo => nil) dbl.foo(42) _expect { expect(dbl).to have_received(:foo) { |arg| _expect(arg).to eq(42) } }.to_not raise_error end if RSpec::Support::RubyFeatures.required_kw_args_supported? it "passes if expectations against yielded keyword arguments pass" do binding.eval(<<-RUBY, __FILE__, __LINE__) dbl = double(:foo => nil) yielded = [] dbl.foo(a: 1) expect(dbl).to have_received(:foo) do |a:| yielded << a end _expect(yielded).to eq([1]) RUBY end end it "fails if expectations against the yielded arguments fail" do dbl = double(:foo => nil) dbl.foo(43) _expect { expect(dbl).to have_received(:foo) { |arg| _expect(arg).to eq(42) } }.to raise_error(RSpec::Expectations::ExpectationNotMetError) end it 'gives precedence to a `{ ... }` block when both forms are provided ' \ 'since that form actually binds to `receive`' do dbl = double(:foo => nil) called = [] dbl.foo expect(dbl).to have_received(:foo) { called << :curly } do called << :do_end end _expect(called).to include(:curly) _expect(called).not_to include(:do_end) end it 'forwards any block passed during method invocation to the `have_received` block' do dbl = spy block = lambda {} dbl.foo(&block) expect(dbl).to have_received(:foo) do |&passed_block| _expect(passed_block).to be block end end it 'resets expectations on class methods when mocks are reset' do dbl = Object allow(dbl).to receive(:expected_method) dbl.expected_method reset dbl allow(dbl).to receive(:expected_method) _expect { expect(dbl).to have_received(:expected_method) }.to raise_error(/0 times/) end context "with" do it 'passes when the given args match the args used with the message' do dbl = double_with_met_expectation(:expected_method, :expected, :args) expect(dbl).to have_received(:expected_method).with(:expected, :args) end it 'fails when the given args do not match the args used with the message' do dbl = double_with_met_expectation(:expected_method, :expected, :args) _expect { expect(dbl).to have_received(:expected_method).with(:unexpected, :args) }.to raise_error(/with unexpected arguments/) end end it 'generates a useful description' do matcher = have_received(:expected_method).with(:expected_args).once _expect(matcher.description).to eq 'have received expected_method(:expected_args) 1 time' end it 'can generate a description after mocks have been torn down (e.g. when rspec-core requests it)' do matcher = have_received(:expected_method).with(:expected_args).once matcher.matches?(double(:expected_method => 1)) RSpec::Mocks.teardown _expect(matcher.description).to eq 'have received expected_method(:expected_args) 1 time' end it 'produces an error message that matches the expected method if another method was called' do my_spy = spy my_spy.foo(1) my_spy.bar(3) _expect { expect(my_spy).to have_received(:foo).with(3) }.to fail_including("received :foo with unexpected arguments", "expected: (3)", "got: (1)") end context "counts" do let(:the_dbl) { double(:expected_method => nil) } before do the_dbl.expected_method(:one) the_dbl.expected_method(:two) the_dbl.expected_method(:one) end context "when constrained by `with`" do it 'only considers the calls with matching args' do expect(the_dbl).to have_received(:expected_method).with(:one).twice expect(the_dbl).to have_received(:expected_method).with(:two).once end context "when the message is received without any args matching" do it 'includes unmatched args in the error message' do _expect { expect(the_dbl).to have_received(:expected_method).with(:three).once }.to fail_including("expected: (:three)", "got:", "(:one) (2 times)", "(:two) (1 time)") end end context "when the message is received too many times" do it 'includes the counts of calls with matching args in the error message' do _expect { expect(the_dbl).to have_received(:expected_method).with(:one).once }.to fail_including("expected: 1 time", "received: 2 times") end end context "when the message is received too few times" do it 'includes the counts of calls with matching args in the error message' do _expect { expect(the_dbl).to have_received(:expected_method).with(:two).twice }.to fail_including("expected: 2 times", "received: 1 time") end end context "when constrained with grouped arguments `with`" do it 'groups the "got" arguments based on the method call that included them' do dbl = double(:expected_method => nil) dbl.expected_method(:one, :four) dbl.expected_method(:two, :four) dbl.expected_method(:three, :four) dbl.expected_method(:one, :four) dbl.expected_method(:three, :four) dbl.expected_method(:three, :four) _expect { expect(dbl).to have_received(:expected_method).with(:four, :four).once }.to fail_including("expected: (:four, :four)", "got:", "(:one, :four) (2 times)", "(:two, :four) (1 time)", "(:three, :four) (3 times)") end it 'includes single arguments based on the method call that included them' do dbl = double(:expected_method => nil) dbl.expected_method(:one, :four) _expect { expect(dbl).to have_received(:expected_method).with(:three, :four).once }.to fail_including("expected: (:three, :four)", "got: (:one, :four)") end it 'keeps the array combinations distinguished in the group' do dbl = double(:expected_method => nil) dbl.expected_method([:one], :four) dbl.expected_method(:one, [:four]) _expect { expect(dbl).to have_received(:expected_method).with(:one, :four).once }.to fail_including("expected: (:one, :four)", "got:", "([:one], :four)", "(:one, [:four])") end it 'does not group counts on repeated arguments for a single message' do dbl = double(:expected_method => nil) dbl.expected_method(:one, :one, :two) _expect { expect(dbl).to have_received(:expected_method).with(:one, :two, :three).once }.to fail_including("expected: (:one, :two, :three)", "got:", "(:one, :one, :two)") end end end context "exactly" do it 'passes when the message was received the given number of times' do expect(the_dbl).to have_received(:expected_method).exactly(3).times end it 'fails when the message was received more times than expected' do _expect { expect(the_dbl).to have_received(:expected_method).exactly(1).time }.to raise_error(/expected: 1 time.*received: 3 times/m) _expect { expect(the_dbl).to have_received(:expected_method).exactly(2).times }.to raise_error(/expected: 2 times.*received: 3 times/m) _expect { the_dbl.expected_method the_dbl.expected_method expect(the_dbl).to have_received(:expected_method).exactly(2).times }.to raise_error(/expected: 2 times.*received: 5 times/m) end it 'fails when the message was received fewer times' do _expect { expect(the_dbl).to have_received(:expected_method).exactly(4).times }.to raise_error(/expected: 4 times.*received: 3 times/m) end end context 'at_least' do it 'passes when the message was received the given number of times' do expect(the_dbl).to have_received(:expected_method).at_least(3).times end it 'passes when the message was received more times' do expect(the_dbl).to have_received(:expected_method).at_least(2).times end it 'fails when the message was received fewer times' do _expect { expect(the_dbl).to have_received(:expected_method).at_least(4).times }.to raise_error(/expected: at least 4 times.*received: 3 times/m) end end context 'at_most' do it 'passes when the message was received the given number of times' do expect(the_dbl).to have_received(:expected_method).at_most(3).times end it 'passes when the message was received fewer times' do expect(the_dbl).to have_received(:expected_method).at_most(4).times end it 'fails when the message was received more times' do _expect { expect(the_dbl).to have_received(:expected_method).at_most(2).times }.to raise_error(/expected: at most 2 times.*received: 3 times/m) end end context 'once' do it 'passes when the message was received once' do dbl = double(:expected_method => nil) dbl.expected_method expect(dbl).to have_received(:expected_method).once end it 'fails when the message was never received' do dbl = double(:expected_method => nil) _expect { expect(dbl).to have_received(:expected_method).once }.to raise_error(/expected: 1 time.*received: 0 times/m) end it 'fails when the message was received twice' do dbl = double(:expected_method => nil) dbl.expected_method dbl.expected_method _expect { expect(dbl).to have_received(:expected_method).once }.to raise_error(/expected: 1 time.*received: 2 times/m) end end context 'twice' do it 'passes when the message was received twice' do dbl = double(:expected_method => nil) dbl.expected_method dbl.expected_method expect(dbl).to have_received(:expected_method).twice end it 'fails when the message was received once' do dbl = double(:expected_method => nil) dbl.expected_method _expect { expect(dbl).to have_received(:expected_method).twice }.to raise_error(/expected: 2 times.*received: 1 time/m) end it 'fails when the message was received thrice' do dbl = double(:expected_method => nil) dbl.expected_method dbl.expected_method dbl.expected_method _expect { expect(dbl).to have_received(:expected_method).twice }.to raise_error(/expected: 2 times.*received: 3 times/m) end end context 'thrice' do it 'passes when the message was received thrice' do dbl = double(:expected_method => nil) dbl.expected_method dbl.expected_method dbl.expected_method expect(dbl).to have_received(:expected_method).thrice end it 'fails when the message was received less than three times' do dbl = double(:expected_method => nil) dbl.expected_method dbl.expected_method _expect { expect(dbl).to have_received(:expected_method).thrice }.to raise_error(/expected: 3 times.*received: 2 times/m) end it 'fails when the message was received more than three times' do dbl = double(:expected_method => nil) dbl.expected_method dbl.expected_method dbl.expected_method dbl.expected_method _expect { expect(dbl).to have_received(:expected_method).thrice }.to raise_error(/expected: 3 times.*received: 4 times/m) end end end context 'ordered' do let(:the_dbl) { double :one => 1, :two => 2, :three => 3 } it 'passes when the messages were received in order' do the_dbl.one the_dbl.two expect(the_dbl).to have_received(:one).ordered expect(the_dbl).to have_received(:two).ordered end it 'passes with exact receive counts when received in order' do 3.times { the_dbl.one } 2.times { the_dbl.two } the_dbl.three expect(the_dbl).to have_received(:one).thrice.ordered expect(the_dbl).to have_received(:two).twice.ordered expect(the_dbl).to have_received(:three).once.ordered end it 'passes with at most receive counts when received in order', :ordered_and_vague_counts_unsupported do the_dbl.one the_dbl.one the_dbl.two expect(the_dbl).to have_received(:one).at_most(3).times.ordered expect(the_dbl).to have_received(:one).at_most(:thrice).times.ordered expect(the_dbl).to have_received(:two).once.ordered end it 'passes with at least receive counts when received in order', :ordered_and_vague_counts_unsupported do the_dbl.one the_dbl.one the_dbl.two expect(the_dbl).to have_received(:one).at_least(1).time.ordered expect(the_dbl).to have_received(:two).once.ordered end it 'fails with exact receive counts when received out of order' do the_dbl.one the_dbl.two the_dbl.one _expect { expect(the_dbl).to have_received(:one).twice.ordered expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) end it "fails with at most receive counts when received out of order", :ordered_and_vague_counts_unsupported do the_dbl.one the_dbl.two the_dbl.one _expect { expect(the_dbl).to have_received(:one).at_most(2).times.ordered expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) end it "fails with at least receive counts when received out of order", :ordered_and_vague_counts_unsupported do the_dbl.one the_dbl.two the_dbl.one _expect { expect(the_dbl).to have_received(:one).at_least(1).time.ordered expect(the_dbl).to have_received(:two).once.ordered }.to raise_error(/received :two out of order/m) end it 'fails when the messages are received out of order' do the_dbl.two the_dbl.one _expect { expect(the_dbl).to have_received(:one).ordered expect(the_dbl).to have_received(:two).ordered }.to raise_error(/received :two out of order/m) end context "when used with `with`" do before do the_dbl.one(1) the_dbl.one(2) end it "passes when the order lines up" do expect(the_dbl).to have_received(:one).with(1).ordered expect(the_dbl).to have_received(:one).with(2).ordered end it "fails when the order is not matched" do _expect { expect(the_dbl).to have_received(:one).with(2).ordered expect(the_dbl).to have_received(:one).with(1).ordered }.to fail_with(/received :one out of order/m) end end context "when used on individually allowed messages" do before do allow(the_dbl).to receive(:foo) allow(the_dbl).to receive(:bar) the_dbl.foo the_dbl.bar end it 'passes when the messages were received in order' do expect(the_dbl).to have_received(:foo).ordered expect(the_dbl).to have_received(:bar).ordered end it 'fails when the messages are received out of order' do _expect { expect(the_dbl).to have_received(:bar).ordered expect(the_dbl).to have_received(:foo).ordered }.to raise_error(/received :foo out of order/m) end end end end describe "expect(...).not_to have_received" do it 'passes when the double has not received the given message' do dbl = double_with_unmet_expectation(:expected_method) expect(dbl).not_to have_received(:expected_method) end it 'fails when the double has received the given message' do dbl = double_with_met_expectation(:expected_method) _expect { expect(dbl).not_to have_received(:expected_method) }.to raise_error(/expected: 0 times.*received: 1 time/m) end it "notifies failures via rspec-support's failure notification system" do dbl = double_with_met_expectation(:expected_method) captured = nil RSpec::Support.with_failure_notifier(Proc.new { |e, _opt| captured = e }) do expect(dbl).not_to have_received(:expected_method) end _expect(captured.message).to match(/expected: 0 times.*received: 1 time/m) end it 'fails when the method has not been previously stubbed' do dbl = double _expect { expect(dbl).not_to have_received(:expected_method) }.to raise_error(/method has not been stubbed/) end context "with" do it 'passes when the given args do not match the args used with the message' do dbl = double_with_met_expectation(:expected_method, :expected, :args) expect(dbl).not_to have_received(:expected_method).with(:unexpected, :args) end it 'fails when the given args match the args used with the message' do dbl = double_with_met_expectation(:expected_method, :expected, :args) _expect { expect(dbl).not_to have_received(:expected_method).with(:expected, :args) }.to raise_error(/expected: 0 times.*received: 1 time/m) # TODO: better message end end %w[exactly at_least at_most times once twice].each do |constraint| it "does not allow #{constraint} to be used because it creates confusion" do dbl = double_with_unmet_expectation(:expected_method) _expect { expect(dbl).not_to have_received(:expected_method).send(constraint) }.to raise_error(/can't use #{constraint} when negative/) end end end describe "allow(...).to have_received" do it "fails because it's nonsensical" do _expect { allow(double).to have_received(:some_method) }.to fail_with("Using allow(...) with the `have_received` matcher is not supported as it would have no effect.") end end describe "allow_any_instance_of(...).to have_received" do it "fails because it's nonsensical" do _expect { allow_any_instance_of(double).to have_received(:some_method) }.to fail_with("Using allow_any_instance_of(...) with the `have_received` matcher is not supported.") end end describe "expect_any_instance_of(...).to have_received" do it "fails because we dont want to support it" do _expect { expect_any_instance_of(double).to have_received(:some_method) }.to fail_with("Using expect_any_instance_of(...) with the `have_received` matcher is not supported.") end end describe "expect_any_instance_of(...).not_to have_received" do it "fails because we dont want to support it" do _expect { expect_any_instance_of(double).not_to have_received(:some_method) }.to fail_with("Using expect_any_instance_of(...) with the `have_received` matcher is not supported.") end end def double_with_met_expectation(method_name, *args) double = double_with_unmet_expectation(method_name) meet_expectation(double, method_name, *args) end def null_object_with_met_expectation(method_name, *args) meet_expectation(double.as_null_object, method_name, *args) end def meet_expectation(double, method_name, *args) double.send(method_name, *args) double end def double_with_unmet_expectation(method_name) double('double', method_name => true) end end RSpec.describe Matchers::HaveReceived, "when used in a context that has rspec-mocks and rspec-expectations available" do include_examples Matchers::HaveReceived do # Override `fail_including` for this context, since `have_received` is a normal # rspec-expectations matcher, the error class is different. def fail_including(*snippets) raise_error(RSpec::Expectations::ExpectationNotMetError, a_string_including(*snippets)) end end end RSpec.describe Matchers::HaveReceived, "when used in a context that has only rspec-mocks available" do # We use a delegator here so that matchers can still be created # via the `RSpec::Matchers` methods. This works because we # instantiate `MocksScope` with the example group instance, so # all undefined methods (including matcher methods) forward to it. # However, `RSpec::Mocks::ExampleMethods` defines `expect` so instances # of this class use the version of `expect` defined in rspec-mocks, not # the one from rspec-expectations. class MocksScope include RSpec::Mocks::ExampleMethods def initialize(example_group) @example_group = example_group end def method_missing(*args, &block) @example_group.__send__(*args, &block) end end # Redefine `it` so that we can eval each example in a special scope # to simulate rspec-expectations not being loaded. def self.it(*args, &block) # Necessary so line-number filtering works... args << {} unless Hash === args.last args.last[:caller] = caller # delegate to the normal `it`... super(*args) do # ...but eval the block in a special scope that has `expect` # from rspec-mocks, not from rspec-expectations. MocksScope.new(self).instance_exec(&block) end end include_examples Matchers::HaveReceived end end end rspec-mocks-3.13.0/spec/rspec/mocks/matchers/receive_message_chain_spec.rb000066400000000000000000000210221455767030500266540ustar00rootroot00000000000000module RSpec::Mocks::Matchers RSpec.describe "receive_message_chain" do let(:object) { double(:object) } context "with only the expect syntax enabled" do include_context "with syntax", :expect it "errors with a negative allowance" do expect { allow(object).not_to receive_message_chain(:to_a) }.to raise_error(RSpec::Mocks::NegationUnsupportedError) end it "errors with a negative expectation" do expect { expect(object).not_to receive_message_chain(:to_a) }.to raise_error(RSpec::Mocks::NegationUnsupportedError) end it "errors with a negative any_instance expectation" do expect { expect_any_instance_of(Object).not_to receive_message_chain(:to_a) }.to raise_error(RSpec::Mocks::NegationUnsupportedError) end it "errors with a negative any_instance allowance" do expect { allow_any_instance_of(Object).not_to receive_message_chain(:to_a) }.to raise_error(RSpec::Mocks::NegationUnsupportedError) end it "works with a do block" do allow(object).to receive_message_chain(:to_a, :length) do 3 end expect(object.to_a.length).to eq(3) end it "works with a {} block" do allow(object).to receive_message_chain(:to_a, :length) { 3 } expect(object.to_a.length).to eq(3) end it "gives the { } block precedence over the do block" do allow(object).to receive_message_chain(:to_a, :length) { 3 } do 4 end expect(object.to_a.length).to eq(3) end it "works with and_return" do allow(object).to receive_message_chain(:to_a, :length).and_return(3) expect(object.to_a.length).to eq(3) end it "works with and_invoke" do allow(object).to receive_message_chain(:to_a, :length).and_invoke(lambda { raise "error" }) expect { object.to_a.length }.to raise_error("error") end it "can constrain the return value by the argument to the last call" do allow(object).to receive_message_chain(:one, :plus).with(1) { 2 } allow(object).to receive_message_chain(:one, :plus).with(2) { 3 } expect(object.one.plus(1)).to eq(2) expect(object.one.plus(2)).to eq(3) end it "works with and_call_original", :pending => "See https://github.com/rspec/rspec-mocks/pull/467#issuecomment-28631621" do list = [1, 2, 3] expect(list).to receive_message_chain(:to_a, :length).and_call_original expect(list.to_a.length).to eq(3) end it "fails with and_call_original when the entire chain is not called", :pending => "See https://github.com/rspec/rspec-mocks/pull/467#issuecomment-28631621" do list = [1, 2, 3] expect(list).to receive_message_chain(:to_a, :length).and_call_original expect(list.to_a).to eq([1, 2, 3]) end it "works with and_raise" do allow(object).to receive_message_chain(:to_a, :length).and_raise(StandardError.new("hi")) expect { object.to_a.length }.to raise_error(StandardError, "hi") end it "works with and_throw" do allow(object).to receive_message_chain(:to_a, :length).and_throw(:nope) expect { object.to_a.length }.to throw_symbol(:nope) end it "works with and_yield" do allow(object).to receive_message_chain(:to_a, :length).and_yield(3) expect { |blk| object.to_a.length(&blk) }.to yield_with_args(3) end it "works with a string of messages to chain" do allow(object).to receive_message_chain("to_a.length").and_yield(3) expect { |blk| object.to_a.length(&blk) }.to yield_with_args(3) end it "works with a hash return as the last argument in the chain" do allow(object).to receive_message_chain(:to_a, :length => 3) expect(object.to_a.length).to eq(3) end it "accepts any number of arguments to the stubbed messages" do allow(object).to receive_message_chain(:msg1, :msg2).and_return(:return_value) expect(object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value")).to eq(:return_value) end it "accepts any number of arguments to the stubbed messages with an inline hash return value" do allow(object).to receive_message_chain(:msg1, :msg2 => :return_value) expect(object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value")).to eq(:return_value) end it "raises when expect is used and some of the messages in the chain aren't called" do expect { expect(object).to receive_message_chain(:to_a, :farce, :length => 3) object.to_a verify_all }.to fail end it "raises when expect is used and all but the last message in the chain are called" do expect { expect(object).to receive_message_chain(:foo, :bar, :baz) object.foo.bar verify_all }.to fail end it "does not raise when expect is used and the entire chain is called" do expect { expect(object).to receive_message_chain(:to_a, :length => 3) object.to_a.length verify_all }.not_to raise_error end it "works with allow_any_instance" do o = Object.new allow_any_instance_of(Object).to receive_message_chain(:to_a, :length => 3) expect(o.to_a.length).to eq(3) end it "stubs already stubbed instances when using `allow_any_instance_of`" do o = Object.new allow(o).to receive(:foo).and_return(dbl = double) expect(o.foo).to be(dbl) allow_any_instance_of(Object).to receive_message_chain(:foo, :bar).and_return("bazz") expect(o.foo.bar).to eq("bazz") end it "fails when with expect_any_instance_of is used and the entire chain is not called" do expect { expect_any_instance_of(Object).to receive_message_chain(:to_a, :length => 3) verify_all }.to fail end it "affects previously stubbed instances when `expect_any_instance_of` is called" do o = Object.new allow(o).to receive(:foo).and_return(double) expect_any_instance_of(Object).to receive_message_chain(:foo, :bar => 3) expect(o.foo.bar).to eq(3) end it "passes when with expect_any_instance_of is used and the entire chain is called" do o = Object.new expect_any_instance_of(Object).to receive_message_chain(:to_a, :length => 3) o.to_a.length end it "works with expect where the first level of the chain is already expected" do o = Object.new expect(o).to receive(:foo).and_return(double) expect(o).to receive_message_chain(:foo, :bar, :baz) o.foo.bar.baz end it "works with allow where the first level of the chain is already expected" do o = Object.new expect(o).to receive(:foo).and_return(double) allow(o).to receive_message_chain(:foo, :bar, :baz).and_return(3) expect(o.foo.bar.baz).to eq(3) end it "works with expect where the first level of the chain is already stubbed" do o = Object.new allow(o).to receive(:foo).and_return(double) expect(o).to receive_message_chain(:foo, :bar, :baz) o.foo.bar.baz end it "works with allow where the first level of the chain is already stubbed" do o = Object.new allow(o).to receive(:foo).and_return(double) allow(o).to receive_message_chain(:foo, :bar, :baz).and_return(3) expect(o.foo.bar.baz).to eq(3) end it "provides a matcher description (when passing a string)" do matcher = receive_message_chain("foo.bar.bazz") expect(matcher.description).to eq("receive message chain foo.bar.bazz") end it "provides a matcher description (when passing symbols)" do matcher = receive_message_chain(:foo, :bar, :bazz) expect(matcher.description).to eq("receive message chain foo.bar.bazz") end it "provides a matcher description (when passing symbols and a hash)" do matcher = receive_message_chain(:foo, :bar, :bazz => 3) expect(matcher.description).to eq("receive message chain foo.bar.bazz") end end context "when the expect and should syntaxes are enabled" do include_context "with syntax", [:expect, :should] it "stubs the message correctly" do allow(object).to receive_message_chain(:to_a, :length) expect { object.to_a.length }.not_to raise_error end end end end rspec-mocks-3.13.0/spec/rspec/mocks/matchers/receive_messages_spec.rb000066400000000000000000000126101455767030500257000ustar00rootroot00000000000000module RSpec module Mocks RSpec.shared_examples "complains when given blocks" do it "complains if a { } block is given" do expect { target.to receive_messages(:a => 1) { "implementation" } }.to raise_error "Implementation blocks aren't supported with `receive_messages`" end it "complains if a do; end; block is given" do expect { target.to receive_messages(:a => 1) do "implementation" end }.to raise_error "Implementation blocks aren't supported with `receive_messages`" end end RSpec.shared_examples "handles partially mocked objects correctly" do let(:obj) { Struct.new(:a).new('original') } it "resets partially mocked objects correctly" do target.to receive_messages(:a => 1, :b => 2) expect { reset obj }.to change { obj.a }.from(1).to("original") end end RSpec.describe "allow(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { double "Object" } let(:target) { allow(obj) } it "allows the object to respond to multiple messages" do allow(obj).to receive_messages(:a => 1, :b => 2) expect(obj.a).to eq 1 expect(obj.b).to eq 2 end it_behaves_like "complains when given blocks" it_behaves_like "handles partially mocked objects correctly" end RSpec.describe "allow_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { Object.new } let(:target) { allow_any_instance_of(Object) } it "allows the object to respond to multiple messages" do allow_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) expect(obj.a).to eq 1 expect(obj.b).to eq 2 end it "updates stubs on instances with existing stubs" do allow(obj).to receive(:a).and_return(3) expect(obj.a).to eq(3) allow_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) expect(obj.a).to eq 1 expect(obj.b).to eq 2 end it_behaves_like "complains when given blocks" end RSpec.describe "expect(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { double "Object" } let(:target) { expect(obj) } let(:expectation_error) do failure = nil begin verify_all rescue RSpec::Mocks::MockExpectationError => error failure = error end failure end it "sets up multiple expectations" do expect(obj).to receive_messages(:a => 1, :b => 2) obj.a expect { verify_all }.to fail end it 'fails with a sensible message' do expect(obj).to receive_messages(:a => 1, :b => 2) obj.b expect(expectation_error.to_s).to eq %Q{(Double "Object").a(no args)\n expected: 1 time with any arguments\n received: 0 times} end it 'fails with the correct location' do expect(obj).to receive_messages(:a => 1, :b => 2); line = __LINE__ expect(expectation_error.backtrace[0]).to match(/#{__FILE__}:#{line}/) end it_behaves_like "complains when given blocks" it_behaves_like "handles partially mocked objects correctly" it "provides a matcher description" do messages = { :a => 1, :b => 2 } matcher = receive_messages(messages) expect(matcher.description).to eq("receive messages: #{messages.inspect}") end end RSpec.describe "expect_any_instance_of(...).to receive_messages(:a => 1, :b => 2)" do let(:obj) { Object.new } let(:target) { expect_any_instance_of(Object) } it "sets up multiple expectations" do expect_any_instance_of(Object).to receive_messages(:a => 1, :b => 2) obj.a expect { RSpec::Mocks.space.verify_all }.to fail RSpec::Mocks.space.reset_all end it_behaves_like "complains when given blocks" end RSpec.describe "negative expectation failure" do let(:obj) { Object.new } example "allow(...).to_not receive_messages(:a => 1, :b => 2)" do expect { allow(obj).to_not receive_messages(:a => 1, :b => 2) }.to( raise_error "`allow(...).to_not receive_messages` is not supported " \ "since it doesn't really make sense. What would it even mean?" ) end example "allow_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do expect { allow_any_instance_of(obj).to_not receive_messages(:a => 1, :b => 2) }.to( raise_error "`allow_any_instance_of(...).to_not receive_messages` is not supported " \ "since it doesn't really make sense. What would it even mean?" ) end example "expect(...).to_not receive_messages(:a => 1, :b => 2)" do expect { expect(obj).to_not receive_messages(:a => 1, :b => 2) }.to( raise_error "`expect(...).to_not receive_messages` is not supported " \ "since it doesn't really make sense. What would it even mean?" ) end example "expect_any_instance_of(...).to_not receive_messages(:a => 1, :b => 2)" do expect { expect_any_instance_of(obj).to_not receive_messages(:a => 1, :b => 2) }.to( raise_error "`expect_any_instance_of(...).to_not receive_messages` is not supported " \ "since it doesn't really make sense. What would it even mean?" ) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/matchers/receive_spec.rb000066400000000000000000000643611455767030500240230ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe Matchers::Receive do include_context "with syntax", :expect describe "expectations/allowances on any instance recorders" do include_context "with syntax", [:expect, :should] it "warns about allow(Klass.any_instance).to receive..." do expect(RSpec).to receive(:warning).with(/allow.*any_instance.*is probably not what you meant.*allow_any_instance_of.*instead/) allow(Object.any_instance).to receive(:foo) end it "includes the correct call site in the allow warning" do expect_warning_with_call_site(__FILE__, __LINE__ + 1) allow(Object.any_instance).to receive(:foo) end it "warns about expect(Klass.any_instance).to receive..." do expect(RSpec).to receive(:warning).with(/expect.*any_instance.*is probably not what you meant.*expect_any_instance_of.*instead/) any_instance_proxy = Object.any_instance expect(any_instance_proxy).to receive(:foo) any_instance_proxy.foo end it "includes the correct call site in the expect warning" do any_instance_proxy = Object.any_instance expect_warning_with_call_site(__FILE__, __LINE__ + 1) expect(any_instance_proxy).to receive(:foo) any_instance_proxy.foo end end # FIXME: this is defined here to prevent # "warning: method redefined; discarding old kw_args_method" # because shared examples are evaluated several times. # When we flatten those shared examples in RSpec 4 because # of no "should" syntax, it will become possible to put this # class definition closer to examples that use it. if RSpec::Support::RubyFeatures.required_kw_args_supported? binding.eval(<<-RUBY, __FILE__, __LINE__) class TestObject def kw_args_method(a:, b:); end end RUBY end shared_examples "a receive matcher" do |*options| it 'allows the caller to configure how the subject responds' do wrapped.to receive(:foo).and_return(5) expect(receiver.foo).to eq(5) end it 'allows the caller to constrain the received arguments' do wrapped.to receive(:foo).with(:a) receiver.foo(:a) expect { receiver.foo(:b) }.to raise_error(/received :foo with unexpected arguments/) end it 'allows the caller to constrain the received arguments by matcher' do wrapped.to receive(:foo).with an_instance_of Float expect { receiver.foo(1) }.to raise_error(/expected.*\(an instance of Float\)/) receiver.foo(1.1) end context 'without yielding receiver' do # when `yield_receiver_to_any_instance_implementation_blocks` is `true` # the block arguments are different for `expect` and `expect_any_instance_of` around do |example| previous_value = RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks? RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = false example.run RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = previous_value end it 'allows a `do...end` block implementation to be provided' do wrapped.to receive(:foo) do 4 end expect(receiver.foo).to eq(4) end if RSpec::Support::RubyFeatures.kw_args_supported? binding.eval(<<-RUBY, __FILE__, __LINE__) it 'allows a `do...end` block implementation with keyword args to be provided' do wrapped.to receive(:foo) do |**kwargs| kwargs[:kw] end expect(receiver.foo(kw: :arg)).to eq(:arg) end it 'allows a `do...end` block implementation with optional keyword args to be provided' do wrapped.to receive(:foo) do |kw: :arg| kw end expect(receiver.foo(kw: 1)).to eq(1) end it 'allows a `do...end` block implementation with optional keyword args to be provided' do wrapped.to receive(:foo) do |kw: :arg| kw end expect(receiver.foo).to eq(:arg) end RUBY end if RSpec::Support::RubyFeatures.required_kw_args_supported? binding.eval(<<-RUBY, __FILE__, __LINE__) it 'allows a `do...end` block implementation with required keyword args' do wrapped.to receive(:foo) do |kw:| kw end expect(receiver.foo(kw: :arg)).to eq(:arg) end it "expects to receive keyword args" do dbl = instance_double(TestObject) expect(dbl).to receive(:kw_args_method).with(a: 1, b: 2) dbl.kw_args_method(a: 1, b: 2) end if RUBY_VERSION >= '3.0' it "fails to expect to receive hash with keyword args" do expect { dbl = instance_double(TestObject) expect(dbl).to receive(:kw_args_method).with(a: 1, b: 2) dbl.kw_args_method({a: 1, b: 2}) }.to fail_with do |failure| reset_all expect(failure.message) .to include('expected: ({:a=>1, :b=>2}) (keyword arguments)') .and include('got: ({:a=>1, :b=>2}) (options hash)') end end else it "expects to receive hash with keyword args" do dbl = instance_double(TestObject) expect(dbl).to receive(:kw_args_method).with(a: 1, b: 2) dbl.kw_args_method({a: 1, b: 2}) end end it "expects to receive hash with a hash" do dbl = instance_double(TestObject) expect(dbl).to receive(:kw_args_method).with({a: 1, b: 2}) dbl.kw_args_method({a: 1, b: 2}) end it "expects to receive keyword args with a hash" do dbl = instance_double(TestObject) expect(dbl).to receive(:kw_args_method).with({a: 1, b: 2}) dbl.kw_args_method(a: 1, b: 2) end RUBY end end it 'allows chaining off a `do...end` block implementation to be provided' do wrapped.to receive(:foo) do 4 end.and_return(6) expect(receiver.foo).to eq(6) end it 'allows a `{ ... }` block implementation to be provided' do wrapped.to receive(:foo) { 5 } expect(receiver.foo).to eq(5) end it 'gives precedence to a `{ ... }` block when both forms are provided ' \ 'since that form actually binds to `receive`' do wrapped.to receive(:foo) { :curly } do :do_end end expect(receiver.foo).to eq(:curly) end it 'does not support other matchers', :unless => options.include?(:allow_other_matchers) do expect { wrapped.to eq(3) }.to raise_error(UnsupportedMatcherError) end it 'does support inherited matchers', :unless => options.include?(:allow_other_matchers) do receive_foo = Class.new(RSpec::Mocks::Matchers::Receive).new(:foo, nil) wrapped.to receive_foo receiver.foo end it 'does not get confused by messages being passed as strings and symbols' do wrapped.to receive(:foo).with(1) { :a } wrapped.to receive("foo").with(2) { :b } expect(receiver.foo(1)).to eq(:a) expect(receiver.foo(2)).to eq(:b) end it 'allows do...end blocks to be passed to the fluent interface methods without getting a warning' do expect(RSpec).not_to receive(:warning) wrapped.to receive(:foo).with(1) do :a end expect(receiver.foo(1)).to eq(:a) end it 'makes { } blocks trump do...end blocks when passed to a fluent interface method' do wrapped.to receive(:foo).with(1) { :curly } do :do_end end expect(receiver.foo(1)).to eq(:curly) end end shared_examples "an expect syntax allowance" do |*options| it_behaves_like "a receive matcher", *options it 'does not expect the message to be received' do wrapped.to receive(:foo) expect { verify_all }.not_to raise_error end end shared_examples "an expect syntax negative allowance" do it 'is disabled since this expression is confusing' do expect { wrapped.not_to receive(:foo) }.to raise_error(/not_to receive` is not supported/) expect { wrapped.to_not receive(:foo) }.to raise_error(/to_not receive` is not supported/) end end shared_examples "an expect syntax expectation" do |*options| it_behaves_like "a receive matcher", *options it 'sets up a message expectation that passes if the message is received' do wrapped.to receive(:foo) receiver.foo verify_all end it 'sets up a message expectation that fails if the message is not received' do wrapped.to receive(:foo) expect { verify_all }.to fail end it "reports the line number of expectation of unreceived message", :pending => options.include?(:does_not_report_line_num) do expected_error_line = __LINE__; wrapped.to receive(:foo) expect { verify_all }.to raise_error { |e| expect(e.backtrace.first).to match(/#{File.basename(__FILE__)}:#{expected_error_line}/) } end it "provides a useful matcher description" do matcher = receive(:foo).with(:bar).once wrapped.to matcher receiver.foo(:bar) expect(matcher.description).to start_with("receive foo") end end shared_examples "an expect syntax negative expectation" do it 'sets up a negative message expectation that passes if the message is not received' do wrapped.not_to receive(:foo) verify_all end it 'sets up a negative message expectation that fails if the message is received' do wrapped.not_to receive(:foo) expect_fast_failure_from(receiver, /expected: 0 times.*received: 1 time/m) do receiver.foo end end it 'supports `to_not` as an alias for `not_to`' do wrapped.to_not receive(:foo) expect_fast_failure_from(receiver, /expected: 0 times.*received: 1 time/m) do receiver.foo end end it 'allows the caller to constrain the received arguments' do wrapped.not_to receive(:foo).with(:a) def receiver.method_missing(*); end # a poor man's stub... expect { receiver.foo(:b) }.not_to raise_error expect_fast_failure_from(receiver, /expected: 0 times.*received: 1 time/m) do receiver.foo(:a) end end it 'prevents confusing double-negative expressions involving `never`' do expect { wrapped.not_to receive(:foo).never }.to raise_error(/trying to negate it again/) expect { wrapped.to_not receive(:foo).never }.to raise_error(/trying to negate it again/) end end shared_examples "resets partial mocks cleanly" do let(:klass) { Struct.new(:foo) } let(:object) { klass.new :bar } it "removes the method double" do target.to receive(:foo).and_return(:baz) expect { reset object }.to change { object.foo }.from(:baz).to(:bar) end end shared_examples "resets partial mocks of any instance cleanly" do let(:klass) { Struct.new(:foo) } let(:object) { klass.new :bar } it "removes the method double" do target.to receive(:foo).and_return(:baz) expect { verify_all }.to change { object.foo }.from(:baz).to(:bar) end end shared_examples "handles frozen objects cleanly" do let(:klass) { Struct.new(:foo) } let(:object) { klass.new :bar } context "when adding the method double" do it "raises clear error" do object.freeze expect { target.to receive(:foo).and_return(:baz) }.to raise_error(ArgumentError, /Cannot proxy frozen objects/) end end context "when removing the method double" do before do if RUBY_VERSION < "2.2" && RUBY_VERSION >= "2.0" && RSpec::Support::Ruby.mri? pending "Does not work on 2.0-2.1 because frozen structs can have methods restored" end end it "warns about being unable to remove the method double" do target.to receive(:foo).and_return(:baz) expect_warning_without_call_site(/rspec-mocks was unable to restore the original `foo` method on #{object.inspect}/) object.freeze reset object end it "includes the spec location in the warning" do line = __LINE__ - 1 target.to receive(:foo).and_return(:baz) expect_warning_without_call_site(/#{RSpec::Core::Metadata.relative_path(__FILE__)}:#{line}/) object.freeze reset object end end context "with fake frozen object" do let(:klass) { Struct.new(:foo, :frozen?, :freeze) } let(:object) { klass.new :bar, true } it "allows the caller to configure how the subject responds" do object.freeze target.to receive(:foo).and_return(5) expect(object.foo).to eq(5) expect { reset object }.to change { object.foo }.from(5).to(:bar) end end end describe "allow(...).to receive" do it_behaves_like "an expect syntax allowance" do let(:receiver) { double } let(:wrapped) { allow(receiver) } end it_behaves_like "resets partial mocks cleanly" do let(:target) { allow(object) } end it_behaves_like "handles frozen objects cleanly" do let(:target) { allow(object) } end context 'ordered with receive counts' do specify 'is not supported' do a_dbl = double expect_warning_with_call_site(__FILE__, __LINE__ + 1) allow(a_dbl).to receive(:one).ordered end end context 'on a class method, from a class with subclasses' do let(:superclass) { Class.new { def self.foo; "foo"; end }} let(:subclass_redef) { Class.new(superclass) { def self.foo; ".foo."; end }} let(:subclass_deleg) { Class.new(superclass) { def self.foo; super.upcase; end }} let(:subclass_asis) { Class.new(superclass) } context 'if the method is redefined in the subclass' do it 'does not stub the method in the subclass' do allow(superclass).to receive(:foo) { "foo!!" } expect(superclass.foo).to eq "foo!!" expect(subclass_redef.foo).to eq ".foo." end end context 'if the method is not redefined in the subclass' do it 'stubs the method in the subclass' do allow(superclass).to receive(:foo) { "foo!!" } expect(superclass.foo).to eq "foo!!" expect(subclass_asis.foo).to eq "foo!!" end end it 'creates stub which can be called using `super` in a subclass' do allow(superclass).to receive(:foo) { "foo!!" } expect(subclass_deleg.foo).to eq "FOO!!" end it 'can stub the same method simultaneously in the superclass and subclasses' do allow(subclass_redef).to receive(:foo) { "__foo__" } allow(superclass).to receive(:foo) { "foo!!" } allow(subclass_deleg).to receive(:foo) { "$$foo$$" } expect(subclass_redef.foo).to eq "__foo__" expect(superclass.foo).to eq "foo!!" expect(subclass_deleg.foo).to eq "$$foo$$" end end end describe "allow(...).not_to receive" do it_behaves_like "an expect syntax negative allowance" do let(:wrapped) { allow(double) } end end describe "allow_any_instance_of(...).to receive" do it_behaves_like "an expect syntax allowance" do let(:klass) { Class.new } let(:wrapped) { allow_any_instance_of(klass) } let(:receiver) { klass.new } end it_behaves_like "resets partial mocks of any instance cleanly" do let(:target) { allow_any_instance_of(klass) } end end describe "allow_any_instance_of(...).not_to receive" do it_behaves_like "an expect syntax negative allowance" do let(:wrapped) { allow_any_instance_of(Class.new) } end end describe "expect(...).to receive" do it_behaves_like "an expect syntax expectation", :allow_other_matchers do let(:receiver) { double } let(:wrapped) { expect(receiver) } context "when a message is not received" do it 'sets up a message expectation that formats argument matchers correctly' do wrapped.to receive(:foo).with an_instance_of Float expect { verify_all }.to( raise_error(/expected: 1 time with arguments: \(an instance of Float\)\n\s+received: 0 times$/) ) end end context "when a message is received the wrong number of times" do it "sets up a message expectation that formats argument matchers correctly" do wrapped.to receive(:foo).with(anything, hash_including(:bar => anything)) receiver.foo(1, :bar => 2) receiver.foo(1, :bar => 3) expect { verify_all }.to( raise_error(/received: 2 times with arguments: \(anything, hash_including\(:bar=>"anything"\)\)$/) ) end end end it_behaves_like "resets partial mocks cleanly" do let(:target) { expect(object) } end it_behaves_like "handles frozen objects cleanly" do let(:target) { expect(object) } end context "ordered with receive counts" do let(:dbl) { double(:one => 1, :two => 2) } it "passes with exact receive counts when the ordering is correct" do expect(dbl).to receive(:one).twice.ordered expect(dbl).to receive(:two).once.ordered dbl.one dbl.one dbl.two end it "fails with exact receive counts when the ordering is incorrect" do expect { expect(dbl).to receive(:one).twice.ordered expect(dbl).to receive(:two).once.ordered dbl.one dbl.two dbl.one }.to raise_error(/out of order/) reset_all end it "passes with at least when the ordering is correct" do expect(dbl).to receive(:one).at_least(2).times.ordered expect(dbl).to receive(:two).once.ordered dbl.one dbl.one dbl.one dbl.two end it "fails with at least when the ordering is incorrect", :ordered_and_vague_counts_unsupported do expect { expect(dbl).to receive(:one).at_least(2).times.ordered expect(dbl).to receive(:two).once.ordered dbl.one dbl.two }.to fail reset_all end it "passes with at most when the ordering is correct" do expect(dbl).to receive(:one).at_most(2).times.ordered expect(dbl).to receive(:two).once.ordered dbl.one dbl.two end it "fails with at most when the ordering is incorrect", :ordered_and_vague_counts_unsupported do expect { expect(dbl).to receive(:one).at_most(2).times.ordered expect(dbl).to receive(:two).once.ordered dbl.one dbl.one dbl.one dbl.two }.to fail reset_all end it 'does not result in infinite recursion when `respond_to?` is stubbed' do # Setting a method expectation causes the method to be proxied # RSpec may call #respond_to? when processing a failed expectation # If those internal calls go to the proxied method, that could # result in another failed expectation error, causing infinite loop expect { obj = Object.new expect(obj).to receive(:respond_to?).with('something highly unlikely') obj.respond_to?(:not_what_we_wanted) }.to raise_error(/received :respond_to\? with unexpected arguments/) reset_all end end end describe "expect_any_instance_of(...).to receive" do it_behaves_like "an expect syntax expectation", :does_not_report_line_num do let(:klass) { Class.new } let(:wrapped) { expect_any_instance_of(klass) } let(:receiver) { klass.new } it 'sets up a message expectation that formats argument matchers correctly' do wrapped.to receive(:foo).with an_instance_of Float expect { verify_all }.to raise_error(/should have received the following message\(s\) but didn't/) end end it_behaves_like "resets partial mocks of any instance cleanly" do let(:target) { expect_any_instance_of(klass) } end end describe "expect(...).not_to receive" do it_behaves_like "an expect syntax negative expectation" do let(:receiver) { double } let(:wrapped) { expect(receiver) } end end describe "expect_any_instance_of(...).not_to receive" do it_behaves_like "an expect syntax negative expectation" do let(:klass) { Class.new } let(:wrapped) { expect_any_instance_of(klass) } let(:receiver) { klass.new } end end it 'has a description before being matched' do matcher = receive(:foo) expect(matcher.description).to eq("receive foo") end shared_examples "using rspec-mocks in another test framework" do it 'can use the `expect` syntax' do dbl = double framework.new.instance_exec do expect(dbl).to receive(:foo).and_return(3) end expect(dbl.foo).to eq(3) end it 'expects the method to be called when `expect` is used' do dbl = double framework.new.instance_exec do expect(dbl).to receive(:foo) end expect { verify dbl }.to fail end it 'supports `expect(...).not_to receive`' do expect_fast_failure_from(double) do |dbl| framework.new.instance_exec do expect(dbl).not_to receive(:foo) end dbl.foo end end it 'supports `expect(...).to_not receive`' do expect_fast_failure_from(double) do |dbl| framework.new.instance_exec do expect(dbl).to_not receive(:foo) end dbl.foo end end end context "when used in a test framework without rspec-expectations" do let(:framework) do Class.new do include RSpec::Mocks::ExampleMethods def eq(_) double("MyMatcher") end end end it_behaves_like "using rspec-mocks in another test framework" it 'cannot use `expect` with another matcher' do expect { framework.new.instance_exec do expect(3).to eq(3) end }.to raise_error(/only the `receive`, `have_received` and `receive_messages` matchers are supported with `expect\(...\).to`/) end it 'can toggle the available syntax' do expect(framework.new).to respond_to(:expect) RSpec::Mocks.configuration.syntax = :should expect(framework.new).not_to respond_to(:expect) RSpec::Mocks.configuration.syntax = :expect expect(framework.new).to respond_to(:expect) end after { RSpec::Mocks.configuration.syntax = :expect } end context "when rspec-expectations is included in the test framework first" do before do # the examples here assume `expect` is define in RSpec::Matchers... expect(RSpec::Matchers.method_defined?(:expect)).to be_truthy end let(:framework) do Class.new do include RSpec::Matchers include RSpec::Mocks::ExampleMethods end end it_behaves_like "using rspec-mocks in another test framework" it 'can use `expect` with any matcher' do framework.new.instance_exec do expect(3).to eq(3) end end it 'with a nonsense allowance it fails with a reasonable error message' do expect { allow(true).not_to be_nil }.to raise_error(start_with("`allow(...).not_to be_nil` is not supported since it doesn't really make sense")) end end context "when rspec-expectations is included in the test framework last" do before do # the examples here assume `expect` is define in RSpec::Matchers... expect(RSpec::Matchers.method_defined?(:expect)).to be_truthy end let(:framework) do Class.new do include RSpec::Mocks::ExampleMethods include RSpec::Matchers end end it_behaves_like "using rspec-mocks in another test framework" it 'can use `expect` with any matcher' do framework.new.instance_exec do expect(3).to eq(3) end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/message_expectation_string_representation_spec.rb000066400000000000000000000026041455767030500313220ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe MessageExpectation, "has a nice string representation" do let(:test_double) { double } example "for a raw message expectation on a test double" do expect(allow(test_double).to receive(:foo)).to have_string_representation( "#.foo(any arguments)>" ) end example "for a raw message expectation on a partial double" do expect(allow("partial double".dup).to receive(:foo)).to have_string_representation( '#' ) end example "for a message expectation constrained by `with`" do expect(allow(test_double).to receive(:foo).with(1, a_kind_of(String), any_args)).to have_string_representation( "#.foo(1, a kind of String, *(any args))>" ) end RSpec::Matchers.define :have_string_representation do |expected_representation| match do |object| values_match?(expected_representation, object.to_s) && object.to_s == object.inspect end failure_message do |object| "expected string representation: #{expected_representation}\n" \ " but got string representation: #{object.to_s}" end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/methods_spec.rb000066400000000000000000000014501455767030500222240ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "Methods added to every object" do include_context "with syntax", :expect def added_methods host = Class.new orig_instance_methods = host.instance_methods Syntax.enable_should(host) (host.instance_methods - orig_instance_methods).map(&:to_sym) end it 'limits the number of methods that get added to all objects' do # If really necessary, you can add to this list, but long term, # we are hoping to cut down on the number of methods added to all objects expect(added_methods).to match_array([ :as_null_object, :null_object?, :received_message?, :should_not_receive, :should_receive, :stub, :stub_chain, :unstub ]) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/mock_expectation_error_spec.rb000066400000000000000000000005711455767030500253310ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe 'MockExpectationError' do class Foo def self.foo bar rescue StandardError end end it 'is not caught by StandardError rescue blocks' do expect(Foo).not_to receive(:bar) expect_fast_failure_from(Foo) do Foo.foo end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/mock_ordering_spec.rb000066400000000000000000000067451455767030500234170ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "ordering" do before { @double = double("test double") } after { reset @double } it "passes when messages are received in order" do expect(@double).to receive(:one).ordered expect(@double).to receive(:two).ordered expect(@double).to receive(:three).ordered @double.one @double.two @double.three end it "passes when messages are received in order" do allow(@double).to receive(:something) expect(@double).to receive(:one).ordered expect(@double).to receive(:two).ordered expect(@double).to receive(:three).at_least(:once).ordered @double.one @double.two @double.three @double.three end it "passes when messages are received in order across objects" do a = double("a") b = double("b") expect(a).to receive(:one).ordered expect(b).to receive(:two).ordered expect(a).to receive(:three).ordered a.one b.two a.three end it "fails when messages are received out of order (2nd message 1st)" do expect(@double).to receive(:one).ordered expect(@double).to receive(:two).ordered expect { @double.two }.to fail_with "# received :two out of order" end it "fails when messages are received out of order (3rd message 1st)" do expect(@double).to receive(:one).ordered expect(@double).to receive(:two).ordered expect(@double).to receive(:three).ordered @double.one expect { @double.three }.to fail_with "# received :three out of order" end it "fails when messages are received out of order (3rd message 2nd)" do expect(@double).to receive(:one).ordered expect(@double).to receive(:two).ordered expect(@double).to receive(:three).ordered @double.one expect { @double.three }.to fail_with "# received :three out of order" end it "fails when messages are out of order across objects" do a = double("test double") b = double("another test double") expect(a).to receive(:one).ordered expect(b).to receive(:two).ordered expect(a).to receive(:three).ordered a.one expect { a.three }.to fail_with "# received :three out of order" reset a reset b end it "ignores order of non ordered messages" do expect(@double).to receive(:ignored_0) expect(@double).to receive(:ordered_1).ordered expect(@double).to receive(:ignored_1) expect(@double).to receive(:ordered_2).ordered expect(@double).to receive(:ignored_2) expect(@double).to receive(:ignored_3) expect(@double).to receive(:ordered_3).ordered expect(@double).to receive(:ignored_4) @double.ignored_3 @double.ordered_1 @double.ignored_0 @double.ordered_2 @double.ignored_4 @double.ignored_2 @double.ordered_3 @double.ignored_1 verify @double end it "supports duplicate messages" do expect(@double).to receive(:a).ordered expect(@double).to receive(:b).ordered expect(@double).to receive(:a).ordered @double.a @double.b @double.a end end end end rspec-mocks-3.13.0/spec/rspec/mocks/modifying_invoked_expectations_spec.rb000066400000000000000000000024231455767030500270540ustar00rootroot00000000000000require "spec_helper" RSpec.describe "Modifying invoked expectations" do shared_examples_for "a customization on an invoked expectation" do |customization_method, *args| it "raises when the #{customization_method} method is called, indicating the expectation has already been invoked" do dbl = double msg_expectation = expect(dbl).to receive(:foo) expect(dbl.foo).to eq(nil) expect { msg_expectation.__send__(customization_method, *args) }.to raise_error( RSpec::Mocks::MockExpectationAlreadyInvokedError, a_string_including(dbl.inspect, "foo", customization_method.to_s) ) end end it_behaves_like "a customization on an invoked expectation", :with, :some_arg it_behaves_like "a customization on an invoked expectation", :and_return, 1 it_behaves_like "a customization on an invoked expectation", :and_raise, "boom" it_behaves_like "a customization on an invoked expectation", :and_throw, :symbol it_behaves_like "a customization on an invoked expectation", :and_yield, 1 it_behaves_like "a customization on an invoked expectation", :exactly, :once it_behaves_like "a customization on an invoked expectation", :at_least, :once it_behaves_like "a customization on an invoked expectation", :at_most, :once end rspec-mocks-3.13.0/spec/rspec/mocks/multiple_invoke_handler_spec.rb000066400000000000000000000102371455767030500254670ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "a message expectation with multiple invoke handlers and no specified count" do let(:a_double) { double } before(:each) do expect(a_double).to receive(:do_something).and_invoke(lambda { 1 }, lambda { raise "2" }, lambda { 3 }) end it "invokes procs in order" do expect(a_double.do_something).to eq 1 expect { a_double.do_something }.to raise_error("2") expect(a_double.do_something).to eq 3 verify a_double end it "falls back to a previously stubbed value" do allow(a_double).to receive_messages :do_something => :stub_result expect(a_double.do_something).to eq 1 expect { a_double.do_something }.to raise_error("2") expect(a_double.do_something).to eq 3 expect(a_double.do_something).to eq :stub_result end it "fails when there are too few calls (if there is no stub)" do a_double.do_something expect { a_double.do_something }.to raise_error("2") expect { verify a_double }.to fail end it "fails when there are too many calls (if there is no stub)" do a_double.do_something expect { a_double.do_something }.to raise_error("2") a_double.do_something a_double.do_something expect { verify a_double }.to fail end end RSpec.describe "a message expectation with multiple invoke handlers with a specified count equal to the number of values" do let(:a_double) { double } before(:each) do expect(a_double).to receive(:do_something).exactly(3).times.and_invoke(lambda { 1 }, lambda { raise "2" }, lambda { 3 }) end it "returns values in order to consecutive calls" do expect(a_double.do_something).to eq 1 expect { a_double.do_something }.to raise_error("2") expect(a_double.do_something).to eq 3 verify a_double end end RSpec.describe "a message expectation with multiple invoke handlers specifying at_least less than the number of values" do let(:a_double) { double } before { expect(a_double).to receive(:do_something).at_least(:twice).with(no_args).and_invoke(lambda { 11 }, lambda { 22 }) } it "uses the last return value for subsequent calls" do expect(a_double.do_something).to equal(11) expect(a_double.do_something).to equal(22) expect(a_double.do_something).to equal(22) verify a_double end it "fails when called less than the specified number" do expect(a_double.do_something).to equal(11) expect { verify a_double }.to fail end context "when method is stubbed too" do before { allow(a_double).to receive(:do_something).and_invoke lambda { :stub_result } } it "uses the last value for subsequent calls" do expect(a_double.do_something).to equal(11) expect(a_double.do_something).to equal(22) expect(a_double.do_something).to equal(22) verify a_double end it "fails when called less than the specified number" do expect(a_double.do_something).to equal(11) expect { verify a_double }.to fail end end end RSpec.describe "a message expectation with multiple invoke handlers with a specified count larger than the number of values" do let(:a_double) { double } before { expect(a_double).to receive(:do_something).exactly(3).times.and_invoke(lambda { 11 }, lambda { 22 }) } it "uses the last return value for subsequent calls" do expect(a_double.do_something).to equal(11) expect(a_double.do_something).to equal(22) expect(a_double.do_something).to equal(22) verify a_double end it "fails when called less than the specified number" do a_double.do_something a_double.do_something expect { verify a_double }.to fail end it "fails fast when called greater than the specified number" do a_double.do_something a_double.do_something a_double.do_something expect_fast_failure_from(a_double) do a_double.do_something end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/multiple_return_value_spec.rb000066400000000000000000000111441455767030500252100ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "a double stubbed with multiple return values" do let(:a_double) { double } before do allow(a_double).to receive(:foo).and_return(:val_1, nil) end it 'can still set a message expectation with a single return value' do expect(a_double).to receive(:foo).once.and_return(:val_1) expect(a_double.foo).to eq(:val_1) end end RSpec.describe "a message expectation with multiple return values and no specified count" do before(:each) do @double = double @return_values = [1, 2, 3] expect(@double).to receive(:do_something).and_return(@return_values[0], @return_values[1], @return_values[2]) end it "returns values in order" do expect(@double.do_something).to eq @return_values[0] expect(@double.do_something).to eq @return_values[1] expect(@double.do_something).to eq @return_values[2] verify @double end it "falls back to a previously stubbed value" do allow(@double).to receive_messages :do_something => :stub_result expect(@double.do_something).to eq @return_values[0] expect(@double.do_something).to eq @return_values[1] expect(@double.do_something).to eq @return_values[2] expect(@double.do_something).to eq :stub_result end it "fails when there are too few calls (if there is no stub)" do @double.do_something @double.do_something expect { verify @double }.to fail end it "fails when there are too many calls (if there is no stub)" do @double.do_something @double.do_something @double.do_something @double.do_something expect { verify @double }.to fail end end RSpec.describe "a message expectation with multiple return values with a specified count equal to the number of values" do before(:each) do @double = double @return_values = [1, 2, 3] expect(@double).to receive(:do_something).exactly(3).times.and_return(@return_values[0], @return_values[1], @return_values[2]) end it "returns values in order to consecutive calls" do expect(@double.do_something).to eq @return_values[0] expect(@double.do_something).to eq @return_values[1] expect(@double.do_something).to eq @return_values[2] verify @double end end RSpec.describe "a message expectation with multiple return values specifying at_least less than the number of values" do before(:each) do @double = double expect(@double).to receive(:do_something).at_least(:twice).with(no_args).and_return(11, 22) end it "uses the last return value for subsequent calls" do expect(@double.do_something).to equal(11) expect(@double.do_something).to equal(22) expect(@double.do_something).to equal(22) verify @double end it "fails when called less than the specified number" do expect(@double.do_something).to equal(11) expect { verify @double }.to fail end context "when method is stubbed too" do before { allow(@double).to receive(:do_something).and_return :stub_result } it "uses the last value for subsequent calls" do expect(@double.do_something).to equal(11) expect(@double.do_something).to equal(22) expect(@double.do_something).to equal(22) verify @double end it "fails when called less than the specified number" do expect(@double.do_something).to equal(11) expect { verify @double }.to fail end end end RSpec.describe "a message expectation with multiple return values with a specified count larger than the number of values" do before(:each) do @double = RSpec::Mocks::Double.new("double") expect(@double).to receive(:do_something).exactly(3).times.and_return(11, 22) end it "uses the last return value for subsequent calls" do expect(@double.do_something).to equal(11) expect(@double.do_something).to equal(22) expect(@double.do_something).to equal(22) verify @double end it "fails when called less than the specified number" do @double.do_something @double.do_something expect { verify @double }.to fail end it "fails fast when called greater than the specified number" do @double.do_something @double.do_something @double.do_something expect_fast_failure_from(@double) do @double.do_something end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/mutate_const_spec.rb000066400000000000000000000604321455767030500232730ustar00rootroot00000000000000TOP_LEVEL_VALUE_CONST = 7 class TestClass M = :m N = :n class Nested class NestedEvenMore end end end class TestClassThatDefinesSend C = :c def self.send end end class TestSubClass < TestClass P = :p end module RSpec module Mocks RSpec.describe "Constant Mutating" do include RSpec::Support::RecursiveConstMethods def reset_rspec_mocks ::RSpec::Mocks.space.reset_all end shared_context "constant example methods" do |const_name| define_method :const do recursive_const_get(const_name) end define_method :parent_const do recursive_const_get("Object::" + const_name.sub(/(::)?[^:]+\z/, '')) end define_method :last_const_part do const_name.split('::').last end end shared_examples "loaded constant stubbing" do |const_name| include_context "constant example methods", const_name let!(:original_const_value) { const } after { change_const_value_to(original_const_value) } def change_const_value_to(value) parent_const.__send__(:remove_const, last_const_part) parent_const.const_set(last_const_part, value) end it 'allows it to be stubbed' do expect(const).not_to eq(7) stub_const(const_name, 7) expect(const).to eq(7) end it 'resets it to its original value when rspec clears its mocks' do original_value = const expect(original_value).not_to eq(:a) stub_const(const_name, :a) reset_rspec_mocks expect(const).to be(original_value) end it 'returns the stubbed value' do expect(stub_const(const_name, 7)).to eq(7) end end shared_examples "loaded constant hiding" do |const_name| before do expect(recursive_const_defined?(const_name)).to be_truthy end it 'allows it to be hidden' do hide_const(const_name) expect(recursive_const_defined?(const_name)).to be_falsey end it 'resets the constant when rspec clear its mocks' do hide_const(const_name) reset_rspec_mocks expect(recursive_const_defined?(const_name)).to be_truthy end it 'returns nil' do expect(hide_const(const_name)).to be_nil end end shared_examples "unloaded constant stubbing" do |const_name| include_context "constant example methods", const_name before do expect(recursive_const_defined?(const_name)).to be_falsey end it 'allows it to be stubbed' do stub_const(const_name, 7) expect(const).to eq(7) end it 'removes the constant when rspec clears its mocks' do stub_const(const_name, 7) reset_rspec_mocks expect(recursive_const_defined?(const_name)).to be_falsey end it 'returns the stubbed value' do expect(stub_const(const_name, 7)).to eq(7) end it 'ignores the :transfer_nested_constants option if passed' do stub = Module.new stub_const(const_name, stub, :transfer_nested_constants => true) expect(stub.constants).to eq([]) end end shared_examples "unloaded constant hiding" do |const_name| include_context "constant example methods", const_name before do expect(recursive_const_defined?(const_name)).to be_falsey end it 'allows it to be hidden, though the operation has no effect' do hide_const(const_name) expect(recursive_const_defined?(const_name)).to be_falsey end it 'remains undefined after rspec clears its mocks' do hide_const(const_name) reset_rspec_mocks expect(recursive_const_defined?(const_name)).to be_falsey end it 'returns nil' do expect(hide_const(const_name)).to be_nil end end describe "#hide_const" do context "for a loaded constant nested in a module that redefines `send`" do it_behaves_like "loaded constant hiding", "TestClassThatDefinesSend::C" end context 'for a loaded nested constant' do it_behaves_like "loaded constant hiding", "TestClass::Nested" end context 'for a loaded constant prefixed with ::' do it_behaves_like 'loaded constant hiding', "::TestClass" end context 'for an unloaded constant with nested name that matches a top-level constant' do it_behaves_like "unloaded constant hiding", "TestClass::Hash" it 'does not hide the top-level constant' do top_level_hash = ::Hash hide_const("TestClass::Hash") expect(::Hash).to equal(top_level_hash) end it 'does not affect the ability to access the top-level constant from nested contexts', :silence_warnings, :if => RUBY_VERSION < '2.5' do top_level_hash = ::Hash hide_const("TestClass::Hash") expect(TestClass::Hash).to equal(top_level_hash) end end context 'for a loaded deeply nested constant' do it_behaves_like "loaded constant hiding", "TestClass::Nested::NestedEvenMore" end context 'for an unloaded unnested constant' do it_behaves_like "unloaded constant hiding", "X" end context 'for an unloaded nested constant' do it_behaves_like "unloaded constant hiding", "X::Y" end it 'can be hidden multiple times but still restores the original value properly' do orig_value = TestClass hide_const("TestClass") hide_const("TestClass") reset_rspec_mocks expect(TestClass).to be(orig_value) end it 'allows a constant to be hidden, then stubbed, restoring it to its original value properly' do orig_value = TOP_LEVEL_VALUE_CONST hide_const("TOP_LEVEL_VALUE_CONST") expect(recursive_const_defined?("TOP_LEVEL_VALUE_CONST")).to be_falsey stub_const("TOP_LEVEL_VALUE_CONST", 12_345) expect(TOP_LEVEL_VALUE_CONST).to eq 12_345 reset_rspec_mocks expect(TOP_LEVEL_VALUE_CONST).to eq orig_value end end describe "#stub_const" do context "for a loaded constant nested in a module that redefines `send`" do it_behaves_like "loaded constant stubbing", "TestClassThatDefinesSend::C" end it "requires a string argument" do expect { stub_const(10, 1) }.to raise_error(ArgumentError, /requires a String/i) end context 'for a loaded unnested constant' do it_behaves_like "loaded constant stubbing", "TestClass" it 'can be stubbed multiple times but still restores the original value properly' do orig_value = TestClass stub1, stub2 = Module.new, Module.new stub_const("TestClass", stub1) stub_const("TestClass", stub2) reset_rspec_mocks expect(TestClass).to be(orig_value) end it 'allows nested constants to be transferred to a stub module' do tc_nested = TestClass::Nested stub = Module.new stub_const("TestClass", stub, :transfer_nested_constants => true) expect(stub::M).to eq(:m) expect(stub::N).to eq(:n) expect(stub::Nested).to be(tc_nested) end it 'removes the transferred constants on reset' do stub = Module.new stub_const("TestClass", stub, :transfer_nested_constants => true) expect { reset_all }.to change { stub.constants }.to([]) end it 'does not transfer nested constants that are inherited from a superclass' do stub = Module.new stub_const("TestSubClass", stub, :transfer_nested_constants => true) expect(stub::P).to eq(:p) expect(defined?(stub::M)).to be_falsey expect(defined?(stub::N)).to be_falsey end it 'raises an error when asked to transfer a nested inherited constant' do original_tsc = TestSubClass expect { stub_const("TestSubClass", Module.new, :transfer_nested_constants => [:M]) }.to raise_error(ArgumentError) expect(TestSubClass).to be(original_tsc) end it 'allows nested constants to be selectively transferred to a stub module' do stub = Module.new stub_const("TestClass", stub, :transfer_nested_constants => [:M, :N]) expect(stub::M).to eq(:m) expect(stub::N).to eq(:n) expect(defined?(stub::Nested)).to be_falsey end it 'raises an error if asked to transfer nested constants but given an object that does not support them' do original_tc = TestClass stub = Object.new expect { stub_const("TestClass", stub, :transfer_nested_constants => true) }.to raise_error(ArgumentError) expect(TestClass).to be(original_tc) expect { stub_const("TestClass", stub, :transfer_nested_constants => [:M]) }.to raise_error(ArgumentError) expect(TestClass).to be(original_tc) end it 'raises an error if asked to transfer nested constants on a constant that does not support nested constants' do stub = Module.new expect { stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => true) }.to raise_error(ArgumentError) expect(TOP_LEVEL_VALUE_CONST).to eq(7) expect { stub_const("TOP_LEVEL_VALUE_CONST", stub, :transfer_nested_constants => [:M]) }.to raise_error(ArgumentError) expect(TOP_LEVEL_VALUE_CONST).to eq(7) end it 'raises an error if asked to transfer a nested constant that is not defined' do original_tc = TestClass expect(defined?(TestClass::V)).to be_falsey stub = Module.new expect { stub_const("TestClass", stub, :transfer_nested_constants => [:V]) }.to raise_error(/cannot transfer nested constant.*V/i) expect(TestClass).to be(original_tc) end describe 'with global transfer_nested_constant option set' do include_context "with isolated configuration" before do RSpec::Mocks.configuration.transfer_nested_constants = true end it 'allows nested constants to be transferred to a stub module' do tc_nested = TestClass::Nested stub = Module.new stub_const("TestClass", stub) expect(stub::M).to eq(:m) expect(stub::N).to eq(:n) expect(stub::Nested).to be(tc_nested) end context "when stubbing a constant that is not a module or a class" do it 'does not attempt to transfer constants' do stub_const("TOP_LEVEL_VALUE_CONST", 4) expect(TOP_LEVEL_VALUE_CONST).to eq(4) end it 'still raises an error when the `:transfer_nested_constants` option is provided' do expect { stub_const("TOP_LEVEL_VALUE_CONST", 4, :transfer_nested_constants => true) }.to raise_error(/cannot transfer nested constant/i) end end end end context 'for a loaded nested constant' do it_behaves_like "loaded constant stubbing", "TestClass::Nested" end context 'for a loaded constant prefixed with ::' do it_behaves_like 'loaded constant stubbing', "::TestClass" end context 'for an unloaded constant prefixed with ::' do it_behaves_like 'unloaded constant stubbing', "::SomeUndefinedConst" end context "for an unloaded constant nested in a module that redefines `send`" do it_behaves_like 'unloaded constant stubbing', "TestClassThatDefinesSend::SomeUndefinedConst" end context 'for an unloaded constant with nested name that matches a top-level constant' do it_behaves_like "unloaded constant stubbing", "TestClass::Hash" end context 'for a loaded deeply nested constant' do it_behaves_like "loaded constant stubbing", "TestClass::Nested::NestedEvenMore" end context 'for an unloaded unnested constant' do it_behaves_like "unloaded constant stubbing", "X" end context 'for an unloaded nested constant' do it_behaves_like "unloaded constant stubbing", "X::Y" it 'removes the root constant when rspec clears its mocks' do expect(defined?(X)).to be_falsey stub_const("X::Y", 7) reset_rspec_mocks expect(defined?(X)).to be_falsey end end context 'for an unloaded deeply nested constant' do it_behaves_like "unloaded constant stubbing", "X::Y::Z" it 'removes the root constant when rspec clears its mocks' do expect(defined?(X)).to be_falsey stub_const("X::Y::Z", 7) reset_rspec_mocks expect(defined?(X)).to be_falsey end end context 'for an unloaded constant nested within a loaded constant' do it_behaves_like "unloaded constant stubbing", "TestClass::X" it 'removes the unloaded constant but leaves the loaded constant when rspec resets its mocks' do expect(defined?(TestClass)).to be_truthy expect(defined?(TestClass::X)).to be_falsey stub_const("TestClass::X", 7) reset_rspec_mocks expect(defined?(TestClass)).to be_truthy expect(defined?(TestClass::X)).to be_falsey end it 'raises a helpful error if it cannot be stubbed due to an intermediary constant that is not a module' do expect(TestClass::M).to be_a(Symbol) expect { stub_const("TestClass::M::X", 5) }.to raise_error(/cannot stub/i) end end context 'for an unloaded constant nested deeply within a deeply nested loaded constant' do it_behaves_like "unloaded constant stubbing", "TestClass::Nested::NestedEvenMore::X::Y::Z" it 'removes the first unloaded constant but leaves the loaded nested constant when rspec resets its mocks' do expect(defined?(TestClass::Nested::NestedEvenMore)).to be_truthy expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_falsey stub_const("TestClass::Nested::NestedEvenMore::X::Y::Z", 7) reset_rspec_mocks expect(defined?(TestClass::Nested::NestedEvenMore)).to be_truthy expect(defined?(TestClass::Nested::NestedEvenMore::X)).to be_falsey end end end end RSpec.describe Constant do describe ".original" do context 'for a previously defined unstubbed constant' do let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has not been mutated") { expect(const).not_to be_mutated } it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } it("indicates it has not been hidden") { expect(const).not_to be_hidden } it("exposes its original value") { expect(const.original_value).to eq(:m) } end context 'for a previously defined stubbed constant' do before { stub_const("TestClass::M", :other) } let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } it("indicates it has not been hidden") { expect(const).not_to be_hidden } it("exposes its original value") { expect(const.original_value).to eq(:m) } end context 'for a previously undefined stubbed constant' do before { stub_const("TestClass::Undefined", :other) } let(:const) { Constant.original("TestClass::Undefined") } it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } it("indicates it has not been hidden") { expect(const).not_to be_hidden } it("returns nil for the original value") { expect(const.original_value).to be_nil } end context 'for a previously undefined parent of a stubbed constant' do before { stub_const("TestClass::UndefinedModule::Undefined", :other) } let(:const) { Constant.original("TestClass::UndefinedModule") } it("exposes its name") { expect(const.name).to eq("TestClass::UndefinedModule") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } it("indicates it has not been hidden") { expect(const).not_to be_hidden } it("returns nil for the original value") { expect(const.original_value).to be_nil } end context 'for a previously undefined unstubbed constant' do let(:const) { Constant.original("TestClass::Undefined") } it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } it("indicates it has not been mutated") { expect(const).not_to be_mutated } it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } it("indicates it has not been hidden") { expect(const).not_to be_hidden } it("returns nil for the original value") { expect(const.original_value).to be_nil } end context 'for a previously defined constant that has been stubbed twice' do before { stub_const("TestClass::M", 1) } before { stub_const("TestClass::M", 2) } let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } it("indicates it has not been hidden") { expect(const).not_to be_hidden } it("exposes its original value") { expect(const.original_value).to eq(:m) } end context 'for a previously undefined constant that has been stubbed twice' do before { stub_const("TestClass::Undefined", 1) } before { stub_const("TestClass::Undefined", 2) } let(:const) { Constant.original("TestClass::Undefined") } it("exposes its name") { expect(const.name).to eq("TestClass::Undefined") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has been stubbed") { expect(const).to be_stubbed } it("indicates it has not been hidden") { expect(const).not_to be_hidden } it("returns nil for the original value") { expect(const.original_value).to be_nil } end context 'for a previously undefined hidden constant' do before { hide_const("SomeUndefinedConst") } let(:const) { Constant.original("SomeUndefinedConst") } it("exposes its name") { expect(const.name).to eq("SomeUndefinedConst") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously undefined") { expect(const).not_to be_previously_defined } it("indicates it has not been mutated") { expect(const).not_to be_mutated } it("indicates it has not not been stubbed") { expect(const).not_to be_stubbed } it("indicates it has not been hidden") { expect(const).not_to be_hidden } it("returns nil for the original value") { expect(const.original_value).to be_nil } end context 'for a previously defined hidden constant' do before { hide_const("TestClass::M") } let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } it("indicates it has been hidden") { expect(const).to be_hidden } it("exposes its original value") { expect(const.original_value).to eq(:m) } end context 'for a previously defined constant that has been hidden twice' do before { hide_const("TestClass::M") } before { hide_const("TestClass::M") } let(:const) { Constant.original("TestClass::M") } it("exposes its name") { expect(const.name).to eq("TestClass::M") } it("indicates the name is valid") { expect(const).to be_valid_name } it("indicates it was previously defined") { expect(const).to be_previously_defined } it("indicates it has been mutated") { expect(const).to be_mutated } it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } it("indicates it has been hidden") { expect(const).to be_hidden } it("exposes its original value") { expect(const.original_value).to eq(:m) } end context "for an invalid const name (such as an anonymous module's `inspect` output)" do let(:mod) { Module.new } let(:const) { Constant.original(mod.inspect) } it("exposes the provided string as the name") { expect(const.name).to eq(mod.inspect) } it("indicates the name is invalid") { expect(const).not_to be_valid_name } it("indicates it was not previously defined") { expect(const).not_to be_previously_defined } it("indicates it has not been mutated") { expect(const).not_to be_mutated } it("indicates it has not been stubbed") { expect(const).not_to be_stubbed } it("indicates it has not been hidden") { expect(const).not_to be_hidden } it("returns nil for its original value") { expect(const.original_value).to be_nil } end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/nil_expectation_warning_spec.rb000066400000000000000000000053431455767030500255000ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "an expectation set on nil" do it "issues a warning with file and line number information" do expect { expect(nil).to receive(:foo) }.to output(a_string_including( "An expectation of `:foo` was set on `nil`", "#{__FILE__}:#{__LINE__ - 3}" )).to_stderr nil.foo end it "issues a warning when the expectation is negative" do expect { expect(nil).not_to receive(:foo) }.to output(a_string_including( "An expectation of `:foo` was set on `nil`", "#{__FILE__}:#{__LINE__ - 3}" )).to_stderr end it 'does not issue a warning when expectations are set to be allowed' do allow_message_expectations_on_nil expect { expect(nil).to receive(:foo) expect(nil).to_not receive(:bar) }.not_to output.to_stderr nil.foo end context 'configured to allow expectation on nil' do include_context 'with isolated configuration' it 'does not issue a warning when expectations are set to be allowed' do RSpec::Mocks.configuration.allow_message_expectations_on_nil = true expect { expect(nil).to receive(:foo) expect(nil).not_to receive(:bar) }.not_to output.to_stderr nil.foo end end context 'configured to disallow expectations on nil' do include_context 'with isolated configuration' it "raises an error when expectations on nil are disallowed" do RSpec::Mocks.configuration.allow_message_expectations_on_nil = false expect { expect(nil).to receive(:foo) }.to raise_error(RSpec::Mocks::MockExpectationError) expect { expect(nil).not_to receive(:bar) }.to raise_error(RSpec::Mocks::MockExpectationError) end end it 'does not call #nil? on a double extra times' do dbl = double expect(dbl).to receive(:nil?).once.and_return(false) dbl.nil? end end RSpec.describe "#allow_message_expectations_on_nil" do include_context "with monkey-patched marshal" it "does not affect subsequent examples" do allow_message_expectations_on_nil RSpec::Mocks.teardown RSpec::Mocks.setup expect { expect(nil).to receive(:foo) }.to output(a_string_including( "An expectation of `:foo` was set on `nil`", "#{__FILE__}:#{__LINE__ - 3}" )).to_stderr nil.foo end it 'doesnt error when marshalled' do allow_message_expectations_on_nil expect(Marshal.dump(nil)).to eq Marshal.dump_without_rspec_mocks(nil) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/null_object_double_spec.rb000066400000000000000000000103101455767030500244060ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "a double _not_ acting as a null object" do before(:each) do @double = double('non-null object') end it "says it does not respond to messages it doesn't understand" do expect(@double).not_to respond_to(:foo) end it "says it responds to messages it does understand" do allow(@double).to receive(:foo) expect(@double).to respond_to(:foo) end it "raises an error when interpolated in a string as an integer" do # Not sure why, but 1.9.2 (but not JRuby --1.9) raises a different # error than 1.8.7 and 1.9.3... expected_error = (RUBY_VERSION == '1.9.2' && RUBY_PLATFORM !~ /java/) ? RSpec::Mocks::MockExpectationError : TypeError expect { "%i" % @double }.to raise_error(expected_error) end end RSpec.describe "a double acting as a null object" do before(:each) do @double = double('null object').as_null_object end it "says it responds to everything" do expect(@double).to respond_to(:any_message_it_gets) end it "allows explicit stubs" do allow(@double).to receive(:foo) { "bar" } expect(@double.foo).to eq("bar") end it "allows explicit expectation" do expect(@double).to receive(:something) @double.something end it 'returns a string from `to_str`' do expect(@double.to_str).to be_a(String) end it 'continues to return self from an explicit expectation' do expect(@double).to receive(:bar) expect(@double.foo.bar).to be(@double) end it 'returns an explicitly stubbed value from an expectation with no implementation' do allow(@double).to receive_messages(:foo => "bar") expect(@double).to receive(:foo) expect(@double.foo).to eq("bar") end it "fails verification when explicit exception not met" do expect { expect(@double).to receive(:something) verify @double }.to fail end it "ignores unexpected methods" do @double.random_call("a", "d", "c") verify @double end it 'allows unexpected message sends using `send`' do val = @double.send(:foo).send(:bar) expect(val).to equal(@double) end it 'allows unexpected message sends using `__send__`' do val = @double.__send__(:foo).__send__(:bar) expect(val).to equal(@double) end it "allows expected message with different args first" do expect(@double).to receive(:message).with(:expected_arg) @double.message(:unexpected_arg) @double.message(:expected_arg) end it "allows expected message with different args second" do expect(@double).to receive(:message).with(:expected_arg) @double.message(:expected_arg) @double.message(:unexpected_arg) end it "can be interpolated in a string as an integer" do # This form of string interpolation calls # @double.to_int.to_int.to_int...etc until it gets an integer, # and thus gets stuck in an infinite loop unless our double # returns an int value from #to_int. expect(("%i" % @double)).to eq("0") end it "does not allow null objects to be used outside of examples" do RSpec::Mocks.teardown expect { @double.some.long.message.chain }.to raise_error(RSpec::Mocks::OutsideOfExampleError) expect { @double.as_null_object }.to raise_error(RSpec::Mocks::OutsideOfExampleError) end end RSpec.describe "#as_null_object" do it "sets the object to null_object" do obj = double('anything').as_null_object expect(obj).to be_null_object end end RSpec.describe "#null_object?" do it "defaults to false" do obj = double('anything') expect(obj).not_to be_null_object end end RSpec.describe "when using the :expect syntax" do include_context "with syntax", :expect it 'still supports null object doubles' do obj = double("foo").as_null_object expect(obj.foo.bar.bazz).to be(obj) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/once_counts_spec.rb000066400000000000000000000054111455767030500231010ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "#once" do before(:each) do @double = double end it "passes when called once" do expect(@double).to receive(:do_something).once @double.do_something verify @double end it "passes when called once with specified args" do expect(@double).to receive(:do_something).once.with("a", "b", "c") @double.do_something("a", "b", "c") verify @double end it "passes when called once with unspecified args" do expect(@double).to receive(:do_something).once @double.do_something("a", "b", "c") verify @double end it "fails when called with wrong args" do expect(@double).to receive(:do_something).once.with("a", "b", "c") expect { @double.do_something("d", "e", "f") }.to fail reset @double end it "fails fast when called twice" do expect(@double).to receive(:do_something).once @double.do_something expect_fast_failure_from(@double) do @double.do_something end end it "fails when not called" do expect(@double).to receive(:do_something).once expect { verify @double }.to fail end context "when called with the wrong number of times with the specified args and also called with different args" do it "mentions the wrong call count in the failure message rather than the different args" do allow(@double).to receive(:do_something) # allow any args... expect(@double).to receive(:do_something).with(:args, 1).once @double.do_something(:args, 2) @double.do_something(:args, 1) expect { # we've grouped these lines because it should probably fail fast # on the first line (since our expectation above only allows one # call with these args), but currently it fails with a confusing # message on verification, and ultimately we care more about # what the message is than when it is raised. Still, it would be # preferrable for the error to be triggered on the first line, # so it'd be good to update this spec to enforce that once we # get the failure message right. @double.do_something(:args, 1) verify @double }.to fail_with(a_string_including("expected: 1 time", "received: 2 times")) end end context "when called with negative expectation" do it "raises an error" do expect { expect(@double).not_to receive(:do_something).once }.to raise_error(/`count` is not supported with negative message expectations/) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/order_group_spec.rb000066400000000000000000000014671455767030500231200ustar00rootroot00000000000000RSpec.describe 'OrderGroup' do let(:order_group) { ::RSpec::Mocks::OrderGroup.new } describe '#consume' do let(:ordered_1) { double :ordered? => true } let(:ordered_2) { double :ordered? => true } let(:unordered) { double :ordered? => false } before do order_group.register unordered order_group.register ordered_1 order_group.register unordered order_group.register ordered_2 order_group.register unordered order_group.register unordered end it 'returns the first ordered? expectation' do expect(order_group.consume).to eq ordered_1 end it 'keeps returning ordered? expectation until all are returned' do expectations = 3.times.map { order_group.consume } expect(expectations).to eq [ordered_1, ordered_2, nil] end end end rspec-mocks-3.13.0/spec/rspec/mocks/partial_double_spec.rb000066400000000000000000000514651455767030500235620ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "A partial double" do let(:object) { Object.new } it 'does not create an any_instance recorder when a message is allowed' do expect { allow(object).to receive(:foo) }.not_to change { RSpec::Mocks.space.any_instance_recorders }.from({}) end it "names the class in the failure message" do expect(object).to receive(:foo) expect do verify object end.to fail_with(/\(#\).foo/) end it "names the class in the failure message when expectation is on class" do expect(Object).to receive(:foo) expect { verify Object }.to fail_with(/Object \(class\)/) end it "does not conflict with @options in the object" do object.instance_exec { @options = Object.new } expect(object).to receive(:blah) object.blah end it 'allows `class` to be stubbed even when `any_instance` has already been used' do # See https://github.com/rspec/rspec-mocks/issues/687 # The infinite recursion code path was only triggered when there were # active any instance recorders in the current example, so we make one here. allow_any_instance_of(Object).to receive(:bar).and_return(2) expect(object.class).not_to eq(String) allow(object).to receive_messages(:foo => 1, :class => String) expect(object.foo).to eq(1) expect(object.class).to eq(String) expect(object.bar).to eq(2) end it 'allows `respond_to?` to be stubbed' do the_klass = Class.new do def call(name) if respond_to?(name) send(name) end end end an_object = the_klass.new expect(an_object).to receive(:respond_to?). with(:my_method).at_least(:once) { true } expect(an_object).to receive(:my_method) an_object.call :my_method end it "can disallow messages from being received" do expect(object).not_to receive(:fuhbar) expect_fast_failure_from( object, /expected\: 0 times with any arguments\n received\: 1 time/ ) do object.fuhbar end end it "can expect a message and set a return value" do expect(object).to receive(:foobar).with(:test_param).and_return(1) expect(object.foobar(:test_param)).to equal(1) end it "can accept a hash as a message argument" do expect(object).to receive(:foobar).with(:key => "value").and_return(1) expect(object.foobar(:key => "value")).to equal(1) end if RSpec::Support::RubyFeatures.required_kw_args_supported? # Use eval to avoid syntax error on 1.8 and 1.9 binding.eval(<<-CODE, __FILE__, __LINE__) it "can accept an inner hash as a message argument" do hash = {:a => {:key => "value"}} expect(object).to receive(:foobar).with(:key => "value").and_return(1) expect(object.foobar(**hash[:a])).to equal(1) end CODE end it "can create a positive message expectation" do expect(expect(object).to receive(:foobar)).not_to be_negative object.foobar end it 'allows a class and a subclass to both be stubbed' do pending "Does not work on 1.8.7 due to singleton method restrictions" if RUBY_VERSION == "1.8.7" && RSpec::Support::Ruby.mri? the_klass = Class.new the_subklass = Class.new(the_klass) allow(the_klass).to receive(:foo).and_return(1) allow(the_subklass).to receive(:foo).and_return(2) expect(the_klass.foo).to eq(1) expect(the_subklass.foo).to eq(2) end it "verifies the method was called when expecting a message" do expect(object).to receive(:foobar).with(:test_param).and_return(1) expect { verify object }.to fail end it "can accept the string form of a message for a positive message expectation" do expect(object).to receive('foobar') object.foobar end it "can accept the string form of a message for a negative message expectation" do expect(object).not_to receive('foobar') expect_fast_failure_from(object) do object.foobar end end it "uses reports nil in the error message" do allow_message_expectations_on_nil nil_var = nil expect(nil_var).to receive(:foobar) expect { verify nil_var }.to raise_error( RSpec::Mocks::MockExpectationError, %Q|(nil).foobar(*(any args))\n expected: 1 time with any arguments\n received: 0 times with any arguments| ) end it "includes the class name in the error when mocking a class method that is called an extra time with the wrong args" do klazz = Class.new do def self.inspect "MyClass" end end expect(klazz).to receive(:bar).with(1) klazz.bar(1) expect { klazz.bar(2) }.to fail_with(/MyClass/) end it "shares message expectations with clone" do expect(object).to receive(:foobar) twin = object.clone twin.foobar expect { verify twin }.not_to raise_error expect { verify object }.not_to raise_error end it "clears message expectations when `dup`ed" do expect(object).to receive(:foobar) duplicate = object.dup expect { duplicate.foobar }.to raise_error(NoMethodError, /foobar/) expect { verify object }.to fail_with(/foobar/) end end RSpec.describe "Using a reopened file object as a partial mock" do let(:file_1) { File.open(File.join("tmp", "file_1"), "w").tap { |f| f.sync = true } } let(:file_2) { File.open(File.join("tmp", "file_2"), "w").tap { |f| f.sync = true } } def read_file(file) File.open(file.path, "r", &:read) end after do file_1.close file_2.close end def expect_output_warning_on_ruby_lt_2 if RUBY_VERSION >= '2.0' yield else expect { yield }.to output(a_string_including( "RSpec could not fully restore", file_1.inspect, "write" )).to_stderr end end it "allows `write` to be stubbed and reset", :unless => Support::Ruby.jruby? do allow(file_1).to receive(:write) file_1.reopen(file_2) expect_output_warning_on_ruby_lt_2 { reset file_1 } expect { # Writing to a file that's been reopened to a 2nd file writes to both files. file_1.write("foo") }.to change { read_file(file_1) }.from("").to("foo"). and change { read_file(file_2) }.from("").to("foo") end end RSpec.describe "Using a partial mock on a proxy object", :if => defined?(::BasicObject) do let(:proxy_class) do Class.new(::BasicObject) do def initialize(target) @target = target end def proxied? true end def respond_to?(name, include_all=false) super || name == :proxied? || @target.respond_to?(name, include_all) end def method_missing(*a) @target.send(*a) end end end let(:wrapped_object) { Object.new } let(:proxy) { proxy_class.new(wrapped_object) } it 'works properly' do expect(proxy).to receive(:proxied?).and_return(false) expect(proxy).not_to be_proxied end it 'does not confuse the proxy and the proxied object' do allow(proxy).to receive(:foo).and_return(:proxy_foo) allow(wrapped_object).to receive(:foo).and_return(:wrapped_foo) expect(proxy.foo).to eq(:proxy_foo) expect(wrapped_object.foo).to eq(:wrapped_foo) end end RSpec.describe "Partially mocking an object that defines ==, after another mock has been defined" do before(:each) do double("existing mock", :foo => :foo) end let(:klass) do Class.new do attr_reader :val def initialize(val) @val = val end def ==(other) @val == other.val end end end it "does not raise an error when stubbing the object" do o = klass.new :foo expect { allow(o).to receive(:bar) }.not_to raise_error end end RSpec.describe "A partial class mock that has been subclassed" do let(:klass) { Class.new } let(:subklass) { Class.new(klass) } it "cleans up stubs during #reset to prevent leakage onto subclasses between examples" do allow(klass).to receive(:new).and_return(:new_foo) expect(subklass.new).to eq :new_foo reset(klass) expect(subklass.new).to be_a(subklass) end describe "stubbing a base class class method" do before do allow(klass).to receive(:find).and_return "stubbed_value" end it "returns the value for the stub on the base class" do expect(klass.find).to eq "stubbed_value" end it "returns the value for the descendent class" do expect(subklass.find).to eq "stubbed_value" end end end RSpec.describe "Method visibility when using partial mocks" do let(:klass) do Class.new do def public_method private_method protected_method end protected def protected_method; end private def private_method; end end end let(:object) { klass.new } it 'keeps public methods public' do expect(object).to receive(:public_method) expect(object.public_methods).to include_method(:public_method) object.public_method end it 'keeps private methods private' do expect(object).to receive(:private_method) expect(object.private_methods).to include_method(:private_method) object.public_method end it 'keeps protected methods protected' do expect(object).to receive(:protected_method) expect(object.protected_methods).to include_method(:protected_method) object.public_method end end RSpec.describe 'when verify_partial_doubles configuration option is set' do include_context "with isolated configuration" let(:klass) do Class.new do def implemented "works" end def initialize(_a, _b) end def respond_to?(method_name, include_all=false) method_name.to_s == "dynamic_method" || super end def method_missing(method_name, *args) if respond_to?(method_name) method_name else super end end private def defined_private_method "works" end end end let(:object) { klass.new(1, 2) } before do RSpec::Mocks.configuration.verify_partial_doubles = true end it 'allows valid methods to be expected' do expect(object).to receive(:implemented).and_call_original expect(object.implemented).to eq("works") end it 'allows private methods to be expected' do expect(object).to receive(:defined_private_method).and_call_original expect(object.send(:defined_private_method)).to eq("works") end it 'can be temporarily supressed' do without_partial_double_verification do expect(object).to receive(:fictitious_method) { 'works' } expect_any_instance_of(klass).to receive(:other_fictitious_method) { 'works' } end expect(object.fictitious_method).to eq 'works' expect(object.other_fictitious_method).to eq 'works' expect { expect(object).to receive(:another_fictitious_method) { 'works' } }.to raise_error RSpec::Mocks::MockExpectationError end it 'can be temporarily supressed and nested' do without_partial_double_verification do without_partial_double_verification do expect(object).to receive(:fictitious_method) { 'works' } end expect(object).to receive(:other_fictitious_method) { 'works' } end expect(object.fictitious_method).to eq 'works' expect(object.other_fictitious_method).to eq 'works' expect { expect(object).to receive(:another_fictitious_method) { 'works' } }.to raise_error RSpec::Mocks::MockExpectationError end specify 'temporarily supressing partial doubles does not affect normal verifying doubles' do without_partial_double_verification do expect { instance_double(Class.new, :fictitious_method => 'works') }.to raise_error RSpec::Mocks::MockExpectationError end end it 'runs the before_verifying_double callbacks before verifying an expectation' do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.before_verifying_doubles(&probe) end expect(object).to receive(:implemented) }.to yield_with_args(have_attributes :target => object) object.implemented end it 'runs the before_verifying_double callbacks before verifying an allowance' do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.before_verifying_doubles(&probe) end allow(object).to receive(:implemented) }.to yield_with_args(have_attributes :target => object) object.implemented end it 'avoids deadlocks when a proxy is accessed from within a `before_verifying_doubles` callback' do msg_klass = Class.new { def message; end; } called_for = [] RSpec.configuration.mock_with(:rspec) do |config| config.before_verifying_doubles do |ref| unless called_for.include? ref.target called_for << ref.target ::RSpec::Mocks.space.proxy_for(ref.target) end end end expect { allow(msg_klass.new).to receive(:message) }.to_not raise_error end context "for a class" do let(:subclass) { Class.new(klass) } it "only runs the `before_verifying_doubles` callback for the class (not for superclasses)" do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.before_verifying_doubles(&probe) end allow(subclass).to receive(:new) }.to yield_successive_args( an_object_having_attributes(:target => subclass) ) end it 'can be temporarily supressed' do without_partial_double_verification do expect(subclass).to receive(:fictitious_method) { 'works' } end expect(subclass.fictitious_method).to eq 'works' expect { expect(subclass).to receive(:another_fictitious_method) { 'works' } }.to raise_error RSpec::Mocks::MockExpectationError end end it 'does not allow a non-existing method to be expected' do prevents { expect(object).to receive(:unimplemented) } end it 'does not allow a spy on unimplemented method' do prevents(/does not implement/) { expect(object).to have_received(:unimplemented) } end it 'verifies arity range when matching arguments' do prevents { expect(object).to receive(:implemented).with('bogus') } reset object end it 'allows a method defined with method_missing to be expected' do expect(object).to receive(:dynamic_method).with('a').and_call_original expect(object.dynamic_method('a')).to eq(:dynamic_method) end it 'allows valid methods to be expected on any_instance' do expect_any_instance_of(klass).to receive(:implemented) object.implemented end it 'allows private methods to be expected on any_instance expectation' do expect_any_instance_of(klass).to receive(:defined_private_method).and_call_original object.send(:defined_private_method) end it 'runs the before_verifying_double callbacks on any_instance before verifying a double allowance' do expect_any_instance_of(klass).to receive(:implemented) expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.before_verifying_doubles(&probe) end object.implemented }.to yield_with_args(have_attributes :target => klass) end it 'runs the before_verifying_double callbacks on any_instance before verifying a double' do allow_any_instance_of(klass).to receive(:implemented) expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.before_verifying_doubles(&probe) end object.implemented }.to yield_with_args(have_attributes :target => klass) end it 'does not allow a non-existing method to be called on any_instance' do prevents(/does not implement/) { expect_any_instance_of(klass).to receive(:unimplemented) } end it 'does not allow missing methods to be called on any_instance' do # This is potentially surprising behaviour, but there is no way for us # to know that this method is valid since we only have class and not an # instance. prevents(/does not implement/) { expect_any_instance_of(klass).to receive(:dynamic_method) } end it 'verifies arity range when receiving a message' do allow(object).to receive(:implemented) expect { object.implemented('bogus') }.to raise_error( ArgumentError, a_string_including("Wrong number of arguments. Expected 0, got 1.") ) end it 'allows the mock to raise an error with yield' do sample_error = Class.new(StandardError) expect(object).to receive(:implemented) { raise sample_error } expect { object.implemented }.to raise_error(sample_error) end it 'allows stubbing and calls the stubbed implementation' do allow(object).to receive(:implemented) { :value } expect(object.implemented).to eq(:value) end context "when `.new` is stubbed" do before do expect(klass.instance_method(:initialize).arity).to eq(2) end it 'uses the method signature from `#initialize` for arg verification' do prevents(/arguments/) { allow(klass).to receive(:new).with(1) } allow(klass).to receive(:new).with(1, 2) end context "on a class that has redefined `new`" do it "uses the method signature of the redefined `new` for arg verification" do subclass = Class.new(klass) do def self.new(_); end end prevents(/arguments/) { allow(subclass).to receive(:new).with(1, 2) } allow(subclass).to receive(:new).with(1) end end context "on a class that has undefined `new`" do it "prevents it from being stubbed" do subclass = Class.new(klass) do class << self undef new end end prevents(/does not implement/) { allow(subclass).to receive(:new).with(1, 2) } end end context "on a class with a private `new`" do it 'uses the method signature from `#initialize` for arg verification' do if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version < '9.2.1.0' pending "Failing on JRuby due to https://github.com/jruby/jruby/issues/2565" end subclass = Class.new(klass) do private_class_method :new end prevents(/arguments/) { allow(subclass).to receive(:new).with(1) } allow(subclass).to receive(:new).with(1, 2) end end context "on a class with a twice-aliased `new`" do it 'uses the method signature from `#initialize` for arg verification', :if => (RUBY_VERSION.to_i >= 3) do subclass = Class.new(klass) do class << self alias_method :_new, :new alias_method :new, :_new end end prevents(/arguments/) { allow(subclass).to receive(:new).with(1) } allow(subclass).to receive(:new).with(1, 2) end end context 'on a class that has redefined `self.method`' do it 'allows the stubbing of :new' do subclass = Class.new(klass) do def self.method(*); end end allow(subclass).to receive(:new) end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb000066400000000000000000000047471455767030500300630ustar00rootroot00000000000000module RSpec::Mocks RSpec.describe "PartialDoubleUsingMocksDirectly" do let(:klass) do Class.new do module MethodMissing remove_method :method_missing rescue nil def method_missing(m, *a, &b) if m == :captured_by_method_missing "response generated by method missing" else super(m, *a, &b) end end end extend MethodMissing include MethodMissing def existing_method :original_value end end end let(:obj) { klass.new } it "fails when expected message is not received" do expect(obj).to receive(:msg) expect { verify obj }.to fail end it "fails when message is received with incorrect args" do expect(obj).to receive(:msg).with(:correct_arg) expect { obj.msg(:incorrect_arg) }.to fail obj.msg(:correct_arg) end it "passes when expected message is received" do expect(obj).to receive(:msg) obj.msg verify obj end it "passes when message is received with correct args" do expect(obj).to receive(:msg).with(:correct_arg) obj.msg(:correct_arg) verify obj end it "restores the original method if it existed" do expect(obj.existing_method).to equal(:original_value) expect(obj).to receive(:existing_method).and_return(:mock_value) expect(obj.existing_method).to equal(:mock_value) verify obj expect(obj.existing_method).to equal(:original_value) end context "with an instance method handled by method_missing" do it "restores the original behavior" do expect(obj.captured_by_method_missing).to eq("response generated by method missing") allow(obj).to receive(:captured_by_method_missing) { "foo" } expect(obj.captured_by_method_missing).to eq("foo") reset obj expect(obj.captured_by_method_missing).to eq("response generated by method missing") end end context "with a class method handled by method_missing" do it "restores the original behavior" do expect(klass.captured_by_method_missing).to eq("response generated by method missing") allow(klass).to receive(:captured_by_method_missing) { "foo" } expect(klass.captured_by_method_missing).to eq("foo") reset klass expect(klass.captured_by_method_missing).to eq("response generated by method missing") end end end end rspec-mocks-3.13.0/spec/rspec/mocks/precise_counts_spec.rb000066400000000000000000000040241455767030500236060ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "PreciseCounts" do before(:each) do @double = double("test double") end it "fails when exactly n times method is called less than n times" do expect(@double).to receive(:do_something).exactly(3).times @double.do_something @double.do_something expect { verify @double }.to fail end it "fails fast when exactly n times method is called more than n times" do expect(@double).to receive(:do_something).exactly(3).times @double.do_something @double.do_something @double.do_something expect_fast_failure_from(@double) do @double.do_something end end it "fails when exactly n times method is never called" do expect(@double).to receive(:do_something).exactly(3).times expect { verify @double }.to fail end it "passes if exactly n times method is called exactly n times" do expect(@double).to receive(:do_something).exactly(3).times @double.do_something @double.do_something @double.do_something verify @double end it "returns the value given by a block when the exactly once method is called" do expect(@double).to receive(:to_s).exactly(:once) { "testing" } expect(@double.to_s).to eq "testing" verify @double end it "passes multiple calls with different args" do expect(@double).to receive(:do_something).once.with(1) expect(@double).to receive(:do_something).once.with(2) @double.do_something(1) @double.do_something(2) verify @double end it "passes multiple calls with different args and counts" do expect(@double).to receive(:do_something).twice.with(1) expect(@double).to receive(:do_something).once.with(2) @double.do_something(1) @double.do_something(2) @double.do_something(1) verify @double end end end end rspec-mocks-3.13.0/spec/rspec/mocks/reraising_eager_raises_spec.rb000066400000000000000000000121461455767030500252610ustar00rootroot00000000000000require "spec_helper" RSpec.describe "Reraising eager raises during the verify step" do it "does not reraise when a double receives a message that hasn't been allowed/expected" do with_unfulfilled_double do |dbl| expect { dbl.foo }.to fail expect { verify_all }.not_to raise_error end end context "when a negative expectation receives a call" do it "reraises during verification" do with_unfulfilled_double do |dbl| expect(dbl).not_to receive(:foo) expect { dbl.foo }.to fail expect { verify_all }.to fail_with(/expected: 0 times with any arguments/) end end it 'notifies both exceptions using the same `:source_id` so `aggregate_failures` can de-dup' do with_unfulfilled_double do |dbl| expect(dbl).not_to receive(:foo) expect { dbl.foo }.to notify_with_same_source_id_as_later_verification end end it 'notifies with a different `source_id` than that for the same double and a different message' do with_unfulfilled_double do |dbl| expect(dbl).not_to receive(:foo) expect { dbl.foo # should trigger first source_id reset(dbl) # Prepare a failing expectation for a different message expect(dbl).not_to receive(:bar) RSpec::Support.with_failure_notifier(Proc.new {}) { dbl.bar } }.not_to notify_with_same_source_id_as_later_verification end end it 'notifies with a different `source_id` than a different double expecting that message' do with_unfulfilled_double do |dbl_1| with_unfulfilled_double do |dbl_2| expect(dbl_1).not_to receive(:foo) expect(dbl_2).not_to receive(:foo) expect { dbl_2.foo }.to fail expect { dbl_1.foo; reset(dbl_1) }.not_to notify_with_same_source_id_as_later_verification end end end end context "when an expectation with a count is exceeded" do def prepare(dbl) expect(dbl).to receive(:foo).exactly(2).times dbl.foo dbl.foo end it "reraises during verification" do with_unfulfilled_double do |dbl| prepare dbl expect { dbl.foo }.to fail expect { verify_all }.to fail_with(/expected: 2 times with any arguments/) end end it 'notifies both exceptions using the same `:source_id` so `aggregate_failures` can de-dup' do with_unfulfilled_double do |dbl| prepare dbl expect { dbl.foo }.to notify_with_same_source_id_as_later_verification end end end context "when an expectation is called with the wrong arguments" do it "reraises during verification" do with_unfulfilled_double do |dbl| expect(dbl).to receive(:foo).with(1, 2, 3) expect { dbl.foo(1, 2, 4) }.to fail expect { verify_all }.to fail_with(/expected: 1 time with arguments: \(1, 2, 3\)/) end end it 'notifies both exceptions using the same `:source_id` so `aggregate_failures` can de-dup' do with_unfulfilled_double do |dbl| expect(dbl).to receive(:foo).with(1, 2, 3) expect { dbl.foo(1, 2, 4) }.to notify_with_same_source_id_as_later_verification end end end context "when an expectation is called out of order", :pending => "Says bar was called 0 times when it was, see: http://git.io/pjTq" do it "reraises during verification" do with_unfulfilled_double do |dbl| expect(dbl).to receive(:foo).ordered expect(dbl).to receive(:bar).ordered expect { dbl.bar }.to fail dbl.foo # satisfy the `foo` expectation so that only the bar one fails below expect { verify_all }.to fail_with(/received :bar out of order/) end end end RSpec::Matchers.define :notify_with_same_source_id_as_later_verification do attr_reader :block match do |block| @block = block block_source_id == verify_all_source_id && block_source_id end match_when_negated do |block| @block = block block_source_id && verify_all_source_id && ( block_source_id != verify_all_source_id ) end supports_block_expectations failure_message do if block_source_id.nil? "expected it to notify with a non-nil source id" else "expected `verify_all` to notify with source_id: #{block_source_id.inspect} but notified with source_id: #{verify_all_source_id.inspect}" end end failure_message_when_negated do if block_source_id.nil? "expected it to notify with a non-nil source id" else "expected `verify_all` to notify with a different source_id but got the same one: #{block_source_id.inspect} / #{verify_all_source_id.inspect}" end end def block_source_id @block_source_id ||= capture_notified_source_id(&block) end def verify_all_source_id @verify_all_source_id ||= capture_notified_source_id { verify_all } end def capture_notified_source_id(&block) source_id = nil notifier = Proc.new { |_err, opt| source_id = opt.fetch(:source_id) } RSpec::Support.with_failure_notifier(notifier, &block) source_id end end end rspec-mocks-3.13.0/spec/rspec/mocks/serialization_spec.rb000066400000000000000000000052041455767030500234370ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "Serialization of mocked objects" do include_context "with monkey-patched marshal" class SerializableObject < Struct.new(:foo, :bar); end def self.with_yaml_loaded(&block) context 'with YAML loaded' do module_exec(&block) end end def self.without_yaml_loaded(&block) context 'without YAML loaded' do before do # We can't really unload yaml, but we can fake it here... hide_const("YAML") Struct.class_exec do alias __old_to_yaml to_yaml undef to_yaml end end module_exec(&block) after do Struct.class_exec do alias to_yaml __old_to_yaml undef __old_to_yaml end end end end let(:serializable_object) { RSpec::Mocks::SerializableObject.new(7, "something") } def set_stub allow(serializable_object).to receive_messages(:bazz => 5) end shared_examples 'normal YAML serialization' do it 'serializes to yaml the same with and without stubbing, using #to_yaml' do expect { set_stub }.to_not change { serializable_object.to_yaml } end it 'serializes to yaml the same with and without stubbing, using YAML.dump' do expect { set_stub }.to_not change { ::YAML.dump(serializable_object) } end end with_yaml_loaded do compiled_with_psych = begin require 'psych' true rescue LoadError false end if compiled_with_psych context 'using Syck as the YAML engine' do before(:each) { ::YAML::ENGINE.yamler = 'syck' } around(:each) { |example| with_isolated_stderr(&example) } it_behaves_like 'normal YAML serialization' end if defined?(::YAML::ENGINE) context 'using Psych as the YAML engine' do before(:each) { ::YAML::ENGINE.yamler = 'psych' } if defined?(::YAML::ENGINE) it_behaves_like 'normal YAML serialization' end else it_behaves_like 'normal YAML serialization' end end without_yaml_loaded do it 'does not add #to_yaml to the stubbed object' do expect(serializable_object).not_to respond_to(:to_yaml) set_stub expect(serializable_object).not_to respond_to(:to_yaml) end end it 'marshals the same with and without stubbing' do expect { set_stub }.to_not change { Marshal.dump(serializable_object) } end end end end rspec-mocks-3.13.0/spec/rspec/mocks/should_syntax_spec.rb000066400000000000000000000462251455767030500234760ustar00rootroot00000000000000require 'support/before_all_shared_example_group' RSpec.describe "Using the legacy should syntax" do include_context "with syntax", [:should, :expect] describe "#received_message?" do let(:dbl) { double("double").as_null_object } it "answers false for received_message? when no messages received" do expect(dbl.received_message?(:message)).to be_falsey end it "answers true for received_message? when message received" do dbl.message expect(dbl.received_message?(:message)).to be_truthy end it "answers true for received_message? when message received with correct args" do dbl.message 1, 2, 3 expect(dbl.received_message?(:message, 1, 2, 3)).to be_truthy end it "answers false for received_message? when message received with incorrect args" do dbl.message 1, 2, 3 expect(dbl.received_message?(:message, 1, 2)).to be_falsey end end describe "#stub" do it "supports options" do double.stub(:foo, :expected_from => "bar") end it 'returns `nil` from all terminal actions to discourage further configuration' do expect(double.stub(:foo).and_return(1)).to be_nil expect(double.stub(:foo).and_raise("boom")).to be_nil expect(double.stub(:foo).and_throw(:foo)).to be_nil end it 'sets up a canned response' do dbl = double dbl.stub(:foo).and_return(3) expect(dbl.foo).to eq(3) end it 'can stub multiple messages using a hash' do dbl = double dbl.stub(:foo => 2, :bar => 1) expect(dbl.foo).to eq(2) expect(dbl.bar).to eq(1) end include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.stub(:foo) end end end describe "#stub_chain" do it 'can stub a sequence of messages' do dbl = double dbl.stub_chain(:foo, :bar, :baz => 17) expect(dbl.foo.bar.baz).to eq(17) expect { dbl.foo.baz.bar }.to fail end include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.stub_chain(:foo, :bar) end end end describe "#unstub" do include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.unstub(:foo) end end it "replaces the stubbed method with the original method" do obj = Object.new def obj.foo; :original; end obj.stub(:foo) obj.unstub(:foo) expect(obj.foo).to eq :original end it "removes all stubs with the supplied method name" do obj = Object.new def obj.foo; :original; end obj.stub(:foo).with(1) obj.stub(:foo).with(2) obj.unstub(:foo) expect(obj.foo).to eq :original end it "does not remove any expectations with the same method name" do obj = Object.new def obj.foo; :original; end obj.should_receive(:foo).with(3).and_return(:three) obj.stub(:foo).with(1) obj.stub(:foo).with(2) obj.unstub(:foo) expect(obj.foo(3)).to eq :three end it "restores the correct implementations when stubbed and unstubbed on a parent and child class" do parent = Class.new child = Class.new(parent) parent.stub(:new) child.stub(:new) parent.unstub(:new) child.unstub(:new) expect(parent.new).to be_an_instance_of parent expect(child.new).to be_an_instance_of child end it "raises a MockExpectationError if the method has not been stubbed" do obj = Object.new expect { obj.unstub(:foo) }.to fail end end describe "#should_receive" do it 'fails on verification if the message is not received' do dbl = double dbl.should_receive(:foo) expect { verify_all }.to fail end it 'does not fail on verification if the message is received' do dbl = double dbl.should_receive(:foo) dbl.foo expect { verify_all }.not_to raise_error end it 'can set a canned response' do dbl = double dbl.should_receive(:bar).and_return(3) expect(dbl.bar).to eq(3) end include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.should_receive(:foo) end end context "with an options hash" do it "reports the file and line submitted with :expected_from" do begin mock = RSpec::Mocks::Double.new("a mock") mock.should_receive(:message, :expected_from => "/path/to/blah.ext:37") verify mock rescue Exception => e ensure expect(e.backtrace.to_s).to match(%r{/path/to/blah.ext:37}m) end end it "uses the message supplied with :message" do expect { m = RSpec::Mocks::Double.new("a mock") m.should_receive(:message, :message => "recebi nada") verify m }.to raise_error("recebi nada") end it "uses the message supplied with :message after a similar stub" do expect { m = RSpec::Mocks::Double.new("a mock") m.stub(:message) m.should_receive(:message, :message => "from mock") verify m }.to raise_error("from mock") end end end describe "#should_not_receive" do it "returns a negative message expectation" do expect(Object.new.should_not_receive(:foobar)).to be_negative end it 'fails when the message is received' do with_unfulfilled_double do |dbl| dbl.should_not_receive(:foo) expect { dbl.foo }.to fail end end it 'does not fail on verification if the message is not received' do dbl = double dbl.should_not_receive(:foo) expect { verify_all }.not_to raise_error end include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.should_not_receive(:foo) end end end describe "#any_instance" do let(:klass) do Class.new do def existing_method; :existing_method_return_value; end def existing_method_with_arguments(_a, _b=nil); :existing_method_with_arguments_return_value; end def another_existing_method; end private def private_method; :private_method_return_value; end end end include_examples "fails in a before(:all) block" do def use_rspec_mocks Object.any_instance.should_receive(:foo) end end it "adds a class to the current space" do expect { klass.any_instance }.to change { RSpec::Mocks.space.any_instance_recorders.size }.by(1) end it 'can stub a method' do klass.any_instance.stub(:foo).and_return(2) expect(klass.new.foo).to eq(2) end it 'can mock a method' do klass.any_instance.should_receive(:foo) klass.new expect { verify_all }.to fail end it 'can get method objects for the fluent interface', :if => RUBY_VERSION.to_f > 1.8 do and_return = klass.any_instance.stub(:foo).method(:and_return) and_return.call(23) expect(klass.new.foo).to eq(23) end it 'affects previously stubbed instances when stubbing a method' do instance = klass.new klass.any_instance.stub(:foo).and_return(2) expect(instance.foo).to eq(2) klass.any_instance.stub(:foo).and_return(1) expect(instance.foo).to eq(1) end it 'affects previously stubbed instances when mocking a method' do instance = klass.new klass.any_instance.stub(:foo).and_return(2) expect(instance.foo).to eq(2) klass.any_instance.should_receive(:foo).and_return(1) expect(instance.foo).to eq(1) end context "invocation order" do describe "#stub" do it "raises an error if 'stub' follows 'with'" do expect { klass.any_instance.with("1").stub(:foo) }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_return'" do expect { klass.any_instance.stub(:foo).and_return(1).with("1") }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_raise'" do expect { klass.any_instance.stub(:foo).and_raise(1).with("1") }.to raise_error(NoMethodError) end it "raises an error if 'with' follows 'and_yield'" do expect { klass.any_instance.stub(:foo).and_yield(1).with("1") }.to raise_error(NoMethodError) end context "behaves as 'every instance'" do let(:super_class) { Class.new { def foo; 'bar'; end } } let(:sub_class) { Class.new(super_class) } it 'handles `unstub` on subclasses' do super_class.any_instance.stub(:foo) sub_class.any_instance.stub(:foo) sub_class.any_instance.unstub(:foo) expect(sub_class.new.foo).to eq("bar") end end end describe "#stub_chain" do it "raises an error if 'stub_chain' follows 'and_return'" do expect { klass.any_instance.and_return("1").stub_chain(:foo, :bar) }.to raise_error(NoMethodError) end context "allows a chain of methods to be stubbed using #stub_chain" do example "given symbols representing the methods" do klass.any_instance.stub_chain(:one, :two, :three).and_return(:four) expect(klass.new.one.two.three).to eq(:four) end example "given a hash as the last argument uses the value as the expected return value" do klass.any_instance.stub_chain(:one, :two, :three => :four) expect(klass.new.one.two.three).to eq(:four) end example "given a string of '.' separated method names representing the chain" do klass.any_instance.stub_chain('one.two.three').and_return(:four) expect(klass.new.one.two.three).to eq(:four) end end it 'affects previously stubbed instances' do instance = klass.new dbl = double klass.any_instance.stub(:foo).and_return(dbl) expect(instance.foo).to eq(dbl) klass.any_instance.stub_chain(:foo, :bar => 3) expect(instance.foo.bar).to eq(3) end end describe "#should_receive" do it "raises an error if 'should_receive' follows 'with'" do expect { klass.any_instance.with("1").should_receive(:foo) }.to raise_error(NoMethodError) end end describe "#should_not_receive" do it "fails if the method is called" do klass.any_instance.should_not_receive(:existing_method) instance = klass.new expect_fast_failure_from(instance) do instance.existing_method end end it "passes if no method is called" do expect { klass.any_instance.should_not_receive(:existing_method) }.to_not raise_error end it "passes if only a different method is called" do klass.any_instance.should_not_receive(:existing_method) expect { klass.new.another_existing_method }.to_not raise_error end context "with constraints" do it "fails if the method is called with the specified parameters" do klass.any_instance.should_not_receive(:existing_method_with_arguments).with(:argument_one, :argument_two) instance = klass.new expect_fast_failure_from(instance) do instance.existing_method_with_arguments(:argument_one, :argument_two) end end it "passes if the method is called with different parameters" do klass.any_instance.should_not_receive(:existing_method_with_arguments).with(:argument_one, :argument_two) expect { klass.new.existing_method_with_arguments(:argument_three, :argument_four) }.to_not raise_error end end context 'when used in combination with should_receive' do it 'passes if only the expected message is received' do klass.any_instance.should_receive(:foo) klass.any_instance.should_not_receive(:bar) klass.new.foo verify_all end end it "prevents confusing double-negative expressions involving `never`" do expect { klass.any_instance.should_not_receive(:not_expected).never }.to raise_error(/trying to negate it again/) end end describe "#unstub" do it "replaces the stubbed method with the original method" do klass.any_instance.stub(:existing_method) klass.any_instance.unstub(:existing_method) expect(klass.new.existing_method).to eq(:existing_method_return_value) end it "removes all stubs with the supplied method name" do klass.any_instance.stub(:existing_method).with(1) klass.any_instance.stub(:existing_method).with(2) klass.any_instance.unstub(:existing_method) expect(klass.new.existing_method).to eq(:existing_method_return_value) end it "removes stubs even if they have already been invoked" do klass.any_instance.stub(:existing_method).and_return(:any_instance_value) obj = klass.new obj.existing_method klass.any_instance.unstub(:existing_method) expect(obj.existing_method).to eq(:existing_method_return_value) end it "removes stubs from sub class after Invocation when super class was originally stubbed" do klass.any_instance.stub(:existing_method).and_return(:any_instance_value) obj = Class.new(klass).new expect(obj.existing_method).to eq(:any_instance_value) klass.any_instance.unstub(:existing_method) expect(obj.existing_method).to eq(:existing_method_return_value) end it "removes stubs set directly on an instance" do klass.any_instance.stub(:existing_method).and_return(:any_instance_value) obj = klass.new obj.stub(:existing_method).and_return(:local_method) klass.any_instance.unstub(:existing_method) expect(obj.existing_method).to eq(:existing_method_return_value) end it "does not remove message expectations set directly on an instance" do klass.any_instance.stub(:existing_method).and_return(:any_instance_value) obj = klass.new obj.should_receive(:existing_method).and_return(:local_method) klass.any_instance.unstub(:existing_method) expect(obj.existing_method).to eq(:local_method) end it "does not remove any expectations with the same method name" do klass.any_instance.should_receive(:existing_method_with_arguments).with(3).and_return(:three) klass.any_instance.stub(:existing_method_with_arguments).with(1) klass.any_instance.stub(:existing_method_with_arguments).with(2) klass.any_instance.unstub(:existing_method_with_arguments) expect(klass.new.existing_method_with_arguments(3)).to eq(:three) end it "raises a MockExpectationError if the method has not been stubbed" do expect { klass.any_instance.unstub(:existing_method) }.to fail_with 'The method `existing_method` was not stubbed or was already unstubbed' end it 'does not get confused about string vs symbol usage for the message' do klass.any_instance.stub(:existing_method) { :stubbed } klass.any_instance.unstub("existing_method") expect(klass.new.existing_method).to eq(:existing_method_return_value) end end end end end RSpec.context "with default syntax configuration" do orig_syntax = nil before(:all) { orig_syntax = RSpec::Mocks.configuration.syntax } after(:all) { RSpec::Mocks.configuration.syntax = orig_syntax } before { RSpec::Mocks.configuration.reset_syntaxes_to_default } if RSpec::Support::RubyFeatures.required_kw_args_supported? let(:expected_arguments) { [ /Using.*without explicitly enabling/, ] } let(:expected_keywords) { {:replacement => "the new `:expect` syntax or explicitly enable `:should`"} } it "it warns about should once, regardless of how many times it is called" do # Use eval to avoid syntax error on 1.8 and 1.9 eval("expect(RSpec).to receive(:deprecate).with(*expected_arguments, **expected_keywords)") o = Object.new o2 = Object.new o.should_receive(:bees) o2.should_receive(:bees) o.bees o2.bees end it "warns about should not once, regardless of how many times it is called" do # Use eval to avoid syntax error on 1.8 and 1.9 eval("expect(RSpec).to receive(:deprecate).with(*expected_arguments, **expected_keywords)") o = Object.new o2 = Object.new o.should_not_receive(:bees) o2.should_not_receive(:bees) end it "warns about stubbing once, regardless of how many times it is called" do # Use eval to avoid syntax error on 1.8 and 1.9 eval("expect(RSpec).to receive(:deprecate).with(*expected_arguments, **expected_keywords)") o = Object.new o2 = Object.new o.stub(:faces) o2.stub(:faces) end else let(:expected_arguments) { [ /Using.*without explicitly enabling/, {:replacement => "the new `:expect` syntax or explicitly enable `:should`"} ] } it "it warns about should once, regardless of how many times it is called" do expect(RSpec).to receive(:deprecate).with(*expected_arguments) o = Object.new o2 = Object.new o.should_receive(:bees) o2.should_receive(:bees) o.bees o2.bees end it "warns about should not once, regardless of how many times it is called" do expect(RSpec).to receive(:deprecate).with(*expected_arguments) o = Object.new o2 = Object.new o.should_not_receive(:bees) o2.should_not_receive(:bees) end it "warns about stubbing once, regardless of how many times it is called" do expect(RSpec).to receive(:deprecate).with(*expected_arguments) o = Object.new o2 = Object.new o.stub(:faces) o2.stub(:faces) end end it "warns about unstubbing once, regardless of how many times it is called" do expect(RSpec).to receive(:deprecate).with(/Using.*without explicitly enabling/, :replacement => "`allow(...).to receive(...).and_call_original` or explicitly enable `:should`") o = Object.new o2 = Object.new allow(o).to receive(:faces) allow(o2).to receive(:faces) o.unstub(:faces) o2.unstub(:faces) end it "doesn't warn about stubbing after a reset and setting should" do expect(RSpec).not_to receive(:deprecate) RSpec::Mocks.configuration.reset_syntaxes_to_default RSpec::Mocks.configuration.syntax = :should o = Object.new o2 = Object.new o.stub(:faces) o2.stub(:faces) end it "includes the call site in the deprecation warning" do obj = Object.new expect_deprecation_with_call_site(__FILE__, __LINE__ + 1) obj.stub(:faces) end end RSpec.context "when the should syntax is enabled on a non-default syntax host" do include_context "with the default mocks syntax" it "continues to warn about the should syntax" do my_host = Class.new expect(RSpec).to receive(:deprecate) RSpec::Mocks::Syntax.enable_should(my_host) o = Object.new o.should_receive(:bees) o.bees end end rspec-mocks-3.13.0/spec/rspec/mocks/space_spec.rb000066400000000000000000000201741455767030500216600ustar00rootroot00000000000000 module RSpec::Mocks RSpec.describe Space do let(:space) { Space.new } let(:dbl_1) { Object.new } let(:dbl_2) { Object.new } describe "#verify_all" do it "verifies all mocks within" do verifies = [] allow(space.proxy_for(dbl_1)).to receive(:verify) { verifies << :dbl_1 } allow(space.proxy_for(dbl_2)).to receive(:verify) { verifies << :dbl_2 } space.verify_all expect(verifies).to match_array([:dbl_1, :dbl_2]) end def define_singleton_method_on_recorder_for(klass, name, &block) recorder = space.any_instance_recorder_for(klass) (class << recorder; self; end).send(:define_method, name, &block) end it 'verifies all any_instance recorders within' do klass_1, klass_2 = Class.new, Class.new verifies = [] # We can't `stub` a method on the recorder because it defines its own `stub`... define_singleton_method_on_recorder_for(klass_1, :verify) { verifies << :klass_1 } define_singleton_method_on_recorder_for(klass_2, :verify) { verifies << :klass_2 } space.verify_all expect(verifies).to match_array([:klass_1, :klass_2]) end it 'does not reset the proxies (as that should be delayed until reset_all)' do proxy = space.proxy_for(dbl_1) reset = false (class << proxy; self; end).__send__(:define_method, :reset) { reset = true } space.verify_all expect(reset).to eq(false) end end describe "#reset_all" do it "resets all mocks within" do resets = [] allow(space.proxy_for(dbl_1)).to receive(:reset) { resets << :dbl_1 } allow(space.proxy_for(dbl_2)).to receive(:reset) { resets << :dbl_2 } space.reset_all expect(resets).to match_array([:dbl_1, :dbl_2]) end it "allows Array#reverse to be stubbed" do # This is a regression check solved in rspec/rspec-mocks#1533 previously # this was not possible without a change to the implementation expect_any_instance_of(Array).to_not receive(:reverse) end end describe "#proxies_of(klass)" do it 'returns proxies' do space.proxy_for("") expect(space.proxies_of(String).map(&:class)).to eq([PartialDoubleProxy]) end def create_generations grandparent_class = Class.new parent_class = Class.new(grandparent_class) child_class = Class.new(parent_class) grandparent = grandparent_class.new parent = parent_class.new child = child_class.new return grandparent, parent, child end it 'returns only the proxies whose object is an instance of the given class' do grandparent, parent, child = create_generations space.proxy_for(grandparent) parent_proxy = space.proxy_for(parent) child_proxy = space.proxy_for(child) expect(space.proxies_of(parent.class)).to contain_exactly(parent_proxy, child_proxy) end it 'looks in the parent space for matching proxies' do _, parent, child = create_generations parent_proxy = space.proxy_for(parent) subspace = space.new_scope child_proxy = subspace.proxy_for(child) expect(subspace.proxies_of(parent.class)).to contain_exactly(parent_proxy, child_proxy) end end it 'tracks proxies in parent and child space separately' do proxy1 = space.proxy_for(Object.new) subspace = space.new_scope proxy2 = subspace.proxy_for(Object.new) expect(space.proxies.values).to include(proxy1) expect(space.proxies.values).not_to include(proxy2) expect(subspace.proxies.values).to include(proxy2) expect(subspace.proxies.values).not_to include(proxy1) end it "only adds an instance once" do m1 = double("mock1") expect { space.ensure_registered(m1) }.to change { space.proxies } expect { space.ensure_registered(m1) }.not_to change { space.proxies } end [:ensure_registered, :proxy_for].each do |method| describe "##{method}" do define_method :get_proxy do |the_space, object| the_space.__send__(method, object) end it 'returns the proxy for the given object' do obj1 = Object.new obj2 = Object.new expect(get_proxy(space, obj1)).to equal(get_proxy(space, obj1)) expect(get_proxy(space, obj2)).to equal(get_proxy(space, obj2)) expect(get_proxy(space, obj1)).not_to equal(get_proxy(space, obj2)) end it 'can still return a proxy from a parent context' do proxy = get_proxy(space, Object) subspace = space.new_scope expect(get_proxy(subspace, Object)).to equal(proxy) end it "does not store a parent's proxy in the child space" do get_proxy(space, Object) subspace = space.new_scope expect { get_proxy(subspace, Object) }.not_to change { subspace.proxies }.from({}) end end end describe "#registered?" do it 'returns true if registered in this space' do space.ensure_registered(Object) expect(space).to be_registered(Object) end it 'returns true if registered in a parent space' do space.ensure_registered(Object) expect(space.new_scope).to be_registered(Object) end it 'returns false if not registered in this or a parent space' do expect(space.new_scope).not_to be_registered(Object) end end describe "#constant_mutator_for" do it 'returns the mutator for the given const name' do the_space = RSpec::Mocks.space stub_const("Foo", 3) stub_const("Bar", 4) expect(the_space.constant_mutator_for("Foo")).to equal(the_space.constant_mutator_for("Foo")) expect(the_space.constant_mutator_for("Bar")).to equal(the_space.constant_mutator_for("Bar")) expect(the_space.constant_mutator_for("Foo")).not_to equal(the_space.constant_mutator_for("Bar")) end it 'can still return a mutator from a parent context' do the_space = RSpec::Mocks.space stub_const("Foo", 3) mutator = the_space.constant_mutator_for("Foo") in_new_space_scope do subspace = RSpec::Mocks.space expect(subspace.constant_mutator_for("Foo")).to equal(mutator) end end end describe "#any_instance_recorder_for" do it 'returns the recorder for the given class' do expect(space.any_instance_recorder_for(String)).to equal(space.any_instance_recorder_for(String)) expect(space.any_instance_recorder_for(Symbol)).to equal(space.any_instance_recorder_for(Symbol)) expect(space.any_instance_recorder_for(String)).not_to equal(space.any_instance_recorder_for(Symbol)) end it 'can still return a recorder from a parent context' do recorder = space.any_instance_recorder_for(String) subspace = space.new_scope expect(subspace.any_instance_recorder_for(String)).to equal(recorder) end it "does not store a parent's proxy in the child space" do space.any_instance_recorder_for(String) subspace = space.new_scope expect { subspace.any_instance_recorder_for(String) }.not_to change { subspace.any_instance_recorders }.from({}) end end it 'can be diffed in a failure when it has references to an error generator via a proxy' do space1 = Space.new space2 = Space.new space1.proxy_for("") space2.proxy_for("") expect { expect(space1).to eq(space2) }.to raise_error(RSpec::Expectations::ExpectationNotMetError, /Diff/) end it 'raises ArgumentError with message if object is symbol' do space1 = Space.new object = :subject expected_message = "Cannot proxy frozen objects. Symbols such as #{object} cannot be mocked or stubbed." expect { space1.proxy_for(object) }.to raise_error(ArgumentError, expected_message) end def in_new_space_scope RSpec::Mocks.setup yield ensure RSpec::Mocks.teardown end end end rspec-mocks-3.13.0/spec/rspec/mocks/spy_spec.rb000066400000000000000000000062161455767030500214010ustar00rootroot00000000000000require "spec_helper" RSpec.describe "the spy family of methods" do describe "spy" do it "responds to arbitrary methods" do expect(spy.respond_to?(:foo)).to be true end it "takes a name" do expect(spy(:bacon_bits).inspect).to include("bacon_bits") end it "records called methods" do expect(spy.tap { |s| s.foo }).to have_received(:foo) end it "takes a hash of method names and return values" do expect(spy(:foo => :bar).foo).to eq(:bar) end it "takes a name and a hash of method names and return values" do expect(spy(:bacon_bits, :foo => :bar).foo).to eq(:bar) end end shared_examples_for "a verifying spy with a foo method" do it "responds to methods on the verified object" do expect(subject.respond_to?(:foo)).to be true end it "does not respond to methods that are not on the verified object" do expect(subject.respond_to?(:other_method)).to be false end it "records called methods" do expect(subject.tap { |s| s.foo }).to have_received(:foo) end it 'fails fast when `have_received` is passed an undefined method name' do expect { expect(subject).to have_received(:bar) }.to fail_including("does not implement") end it 'fails fast when negative `have_received` is passed an undefined method name' do expect { expect(subject).to_not have_received(:bar) }.to fail_including("does not implement") end end describe "instance_spy" do context "when passing a class object" do let(:the_class) do Class.new do def foo 3 end end end subject { instance_spy(the_class) } it_behaves_like "a verifying spy with a foo method" it "takes a class and a hash of method names and return values" do expect(instance_spy(the_class, :foo => :bar).foo).to eq(:bar) end end context "passing a class by string reference" do DummyClass = Class.new do def foo 3 end end let(:the_class) { "DummyClass" } subject { instance_spy(the_class) } it_behaves_like "a verifying spy with a foo method" it "takes a class name string and a hash of method names and return values" do expect(instance_spy(the_class, :foo => :bar).foo).to eq(:bar) end end end describe "object_spy" do let(:the_class) do Class.new do def foo 3 end end end let(:the_instance) { the_class.new } subject { object_spy(the_instance) } it_behaves_like "a verifying spy with a foo method" it "takes an instance and a hash of method names and return values" do expect(object_spy(the_instance, :foo => :bar).foo).to eq(:bar) end end describe "class_spy" do let(:the_class) do Class.new do def self.foo 3 end end end subject { class_spy(the_class) } it_behaves_like "a verifying spy with a foo method" it "takes a class and a hash of method names and return values" do expect(class_spy(the_class, :foo => :bar).foo).to eq(:bar) end end end rspec-mocks-3.13.0/spec/rspec/mocks/standalone_spec.rb000066400000000000000000000012371455767030500227140ustar00rootroot00000000000000require 'rspec/support/spec/in_sub_process' main = self RSpec.describe "Loading rspec/mocks/standalone" do include RSpec::Support::InSubProcess it "exposes the RSpec::Mocks API on `main`" do in_sub_process do require 'rspec/mocks/standalone' main.instance_eval do dbl = double expect(dbl).to receive(:foo) dbl.foo RSpec::Mocks.verify RSpec::Mocks.teardown end end end it "does not infect other objects with the RSpec::Mocks API" do in_sub_process do require 'rspec/mocks/standalone' object = Object.new expect(object).not_to respond_to(:double, :expect) end end end rspec-mocks-3.13.0/spec/rspec/mocks/stash_spec.rb000066400000000000000000000021641455767030500217060ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "only stashing the original method" do let(:klass) do Class.new do def self.foo(_) :original_value end end end it "keeps the original method intact after multiple expectations are added on the same method" do expect(klass).to receive(:foo).with(:fizbaz).and_return(:wowwow) expect(klass).to receive(:foo).with(:bazbar).and_return(:okay) klass.foo(:fizbaz) klass.foo(:bazbar) verify klass reset klass expect(klass.foo(:yeah)).to equal(:original_value) end end RSpec.describe "when a class method is aliased on a subclass and the method is mocked" do it "restores the original aliased public method" do klass = Class.new do class << self alias alternate_new new end end expect(klass).to receive(:alternate_new) expect(klass.alternate_new).to be_nil verify klass reset klass expect(klass.alternate_new).to be_an_instance_of(klass) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/stub_chain_spec.rb000066400000000000000000000150651455767030500227070ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "A chained method stub" do let(:object) { Object.new } it 'does not get confused by symbol vs string usage for the messages' do allow(object).to receive_message_chain(:foo, :bar => 1) allow(object).to receive_message_chain("foo", :bazz => 2) expect(object.foo.bar).to eq(1) expect(object.foo.bazz).to eq(2) end context "with one method in chain" do context "using and_return" do it "returns expected value from chaining only one method call" do allow(object).to receive_message_chain(:msg1).and_return(:return_value) expect(object.msg1).to equal(:return_value) end end context "using a block" do it "returns the correct value" do allow(object).to receive_message_chain(:msg1) { :return_value } expect(object.msg1).to equal(:return_value) end end context "using a hash" do it "returns the value of the key/value pair" do allow(object).to receive_message_chain(:msg1 => :return_value) expect(object.msg1).to equal(:return_value) end end end context "with two methods in chain" do it "accepts any number of arguments to the stubbed messages in the chain" do allow(object).to receive_message_chain(:msg1, :msg2).and_return(:return_value) expect(object.msg1("nonsense", :value).msg2("another", :nonsense, 3.0, "value")).to eq(:return_value) end context "using and_return" do it "returns expected value from chaining two method calls" do allow(object).to receive_message_chain(:msg1, :msg2).and_return(:return_value) expect(object.msg1.msg2).to equal(:return_value) end end context "using a block" do it "returns the correct value" do allow(object).to receive_message_chain(:msg1, :msg2) { :return_value } expect(object.msg1.msg2).to equal(:return_value) end end context "using a hash" do it "returns the value of the key/value pair" do allow(object).to receive_message_chain(:msg1, :msg2 => :return_value) expect(object.msg1.msg2).to equal(:return_value) end end end context "with four methods in chain" do context "using and_return" do it "returns expected value from chaining two method calls" do allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value) expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end end context "using a block" do it "returns the correct value" do allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4) { :return_value } expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end end context "using a hash" do it "returns the value of the key/value pair" do allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4 => :return_value) expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end end context "using a hash with a string key" do it "returns the value of the key/value pair" do allow(object).to receive_message_chain("msg1.msg2.msg3.msg4" => :return_value) expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end end end it "returns expected value from chaining four method calls" do allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4).and_return(:return_value) expect(object.msg1.msg2.msg3.msg4).to equal(:return_value) end context "with messages shared across multiple chains" do context "using and_return" do context "starting with the same message" do it "returns expected value" do allow(object).to receive_message_chain(:msg1, :msg2, :msg3).and_return(:first) allow(object).to receive_message_chain(:msg1, :msg2, :msg4).and_return(:second) expect(object.msg1.msg2.msg3).to equal(:first) expect(object.msg1.msg2.msg4).to equal(:second) end end context "starting with the different messages" do it "returns expected value" do allow(object).to receive_message_chain(:msg1, :msg2, :msg3).and_return(:first) allow(object).to receive_message_chain(:msg4, :msg2, :msg3).and_return(:second) expect(object.msg1.msg2.msg3).to equal(:first) expect(object.msg4.msg2.msg3).to equal(:second) end end end context "using => value" do context "starting with the same message" do it "returns expected value" do allow(object).to receive_message_chain(:msg1, :msg2, :msg3 => :first) allow(object).to receive_message_chain(:msg1, :msg2, :msg4 => :second) expect(object.msg1.msg2.msg3).to equal(:first) expect(object.msg1.msg2.msg4).to equal(:second) end end context "starting with different messages" do it "returns expected value" do allow(object).to receive_message_chain(:msg1, :msg2, :msg3 => :first) allow(object).to receive_message_chain(:msg4, :msg2, :msg3 => :second) expect(object.msg1.msg2.msg3).to equal(:first) expect(object.msg4.msg2.msg3).to equal(:second) end end end end it "returns expected value when chain is a dot separated string, like stub_chain('msg1.msg2.msg3')" do allow(object).to receive_message_chain("msg1.msg2.msg3").and_return(:return_value) expect(object.msg1.msg2.msg3).to equal(:return_value) end it "returns expected value from two chains with shared messages at the beginning" do allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg4).and_return(:first) allow(object).to receive_message_chain(:msg1, :msg2, :msg3, :msg5).and_return(:second) expect(object.msg1.msg2.msg3.msg4).to equal(:first) expect(object.msg1.msg2.msg3.msg5).to equal(:second) end it "handles private instance methods (like Object#select) in the middle of a chain" do allow(object).to receive_message_chain(:msg1, :select, :msg3 => 'answer') expect(object.msg1.select.msg3).to eq 'answer' end end end end rspec-mocks-3.13.0/spec/rspec/mocks/stub_implementation_spec.rb000066400000000000000000000075701455767030500246540ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "stub implementation" do describe "with no args" do it "execs the block when called" do obj = double allow(obj).to receive(:foo) { :bar } expect(obj.foo).to eq :bar end end describe "with one arg" do it "execs the block with that arg when called" do obj = double allow(obj).to receive(:foo) { |given| given } expect(obj.foo(:bar)).to eq :bar end end describe "with variable args" do it "execs the block when called" do obj = double allow(obj).to receive(:foo) { |*given| given.first } expect(obj.foo(:bar)).to eq :bar end end end RSpec.describe "unstubbing with `and_call_original`" do it "replaces the stubbed method with the original method" do obj = Object.new def obj.foo; :original; end allow(obj).to receive(:foo) allow(obj).to receive(:foo).and_call_original expect(obj.foo).to eq :original end it "removes all stubs with the supplied method name" do obj = Object.new def obj.foo; :original; end allow(obj).to receive(:foo).with(1) allow(obj).to receive(:foo).with(2) allow(obj).to receive(:foo).and_call_original expect(obj.foo).to eq :original end it "does not remove any expectations with the same method name" do obj = Object.new def obj.foo; :original; end expect(obj).to receive(:foo).with(3).and_return(:three) allow(obj).to receive(:foo).with(1) allow(obj).to receive(:foo).with(2) allow(obj).to receive(:foo).and_call_original expect(obj.foo(3)).to eq :three end shared_examples_for "stubbing `new` on class objects" do it "restores the correct implementations when stubbed and unstubbed on a parent and child class" do parent = stub_const("Parent", Class.new) child = stub_const("Child", Class.new(parent)) allow(parent).to receive(:new) allow(child).to receive(:new) allow(parent).to receive(:new).and_call_original allow(child).to receive(:new).and_call_original expect(parent.new).to be_an_instance_of parent expect(child.new).to be_an_instance_of child end it "restores the correct implementations when stubbed and unstubbed on a grandparent and grandchild class" do grandparent = stub_const("GrandParent", Class.new) parent = stub_const("Parent", Class.new(grandparent)) child = stub_const("Child", Class.new(parent)) allow(grandparent).to receive(:new) allow(child).to receive(:new) allow(grandparent).to receive(:new).and_call_original allow(child).to receive(:new).and_call_original expect(grandparent.new).to be_an_instance_of grandparent expect(child.new).to be_an_instance_of child end end context "when partial doubles are not verified" do before { expect(RSpec::Mocks.configuration.verify_partial_doubles?).to be false } include_examples "stubbing `new` on class objects" end context "when partial doubles are verified" do include_context "with isolated configuration" before { RSpec::Mocks.configuration.verify_partial_doubles = true } include_examples "stubbing `new` on class objects" if RSpec::Support::RubyFeatures.required_kw_args_supported? binding.eval(<<-RUBY, __FILE__, __LINE__) it "handles keyword arguments correctly" do klass = Class.new do def initialize(kw:) end end allow(klass).to receive(:new).and_call_original klass.new(kw: 42) end RUBY end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/stub_spec.rb000066400000000000000000000457701455767030500215530ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "A method stub" do before(:each) do @class = Class.new do class << self def existing_class_method existing_private_class_method end private def existing_private_class_method :original_value end end def existing_instance_method existing_private_instance_method end private def existing_private_instance_method :original_value end end @instance = @class.new @stub = Object.new end describe "using `and_return`" do it "returns declared value when message is received" do allow(@instance).to receive(:msg).and_return(:return_value) expect(@instance.msg).to equal(:return_value) verify @instance end end it "instructs an instance to respond_to the message" do allow(@instance).to receive(:msg) expect(@instance).to respond_to(:msg) end it "instructs a class object to respond_to the message" do allow(@class).to receive(:msg) expect(@class).to respond_to(:msg) end it "ignores when expected message is received with no args" do allow(@instance).to receive(:msg) @instance.msg expect do verify @instance end.not_to raise_error end it "ignores when message is received with args" do allow(@instance).to receive(:msg) @instance.msg(:an_arg) expect do verify @instance end.not_to raise_error end it "ignores when expected message is not received" do allow(@instance).to receive(:msg) expect do verify @instance end.not_to raise_error end it "handles multiple stubbed methods" do allow(@instance).to receive_messages(:msg1 => 1, :msg2 => 2) expect(@instance.msg1).to eq(1) expect(@instance.msg2).to eq(2) end it "is retained when stubbed object is `clone`d" do allow(@stub).to receive(:foobar).and_return(1) expect(@stub.clone.foobar).to eq(1) end it "is cleared when stubbed object when `dup`ed" do allow(@stub).to receive(:foobar).and_return(1) expect { @stub.dup.foobar }.to raise_error NoMethodError, /foobar/ end it "remains private when it stubs a private instance method" do allow(@instance).to receive(:existing_private_instance_method).and_return(1) expect { @instance.existing_private_instance_method }.to raise_error NoMethodError, /private method `existing_private_instance_method/ end it "remains private when it stubs a private class method" do allow(@class).to receive(:existing_private_class_method).and_return(1) expect { @class.existing_private_class_method }.to raise_error NoMethodError, /private method `existing_private_class_method/ end context "using `with`" do it 'determines which value is returned' do allow(@stub).to receive(:foo).with(1) { :one } allow(@stub).to receive(:foo).with(2) { :two } expect(@stub.foo(2)).to eq(:two) expect(@stub.foo(1)).to eq(:one) end it 'allows differing arities' do allow(@stub).to receive(:foo).with(:two, :args) { :two_args } allow(@stub).to receive(:foo).with(:three, :args, :total) { :three_args_total } expect(@stub.foo(:two, :args)).to eq(:two_args) expect(@stub.foo(:three, :args, :total)).to eq(:three_args_total) end end context "when the stubbed method is called" do it "does not call any methods on the passed args, since that could mutate them", :issue => 892 do recorder = Class.new(defined?(::BasicObject) ? ::BasicObject : ::Object) do def called_methods @called_methods ||= [] end def method_missing(name, *) called_methods << name self end end.new allow(@stub).to receive(:foo) expect { @stub.foo(recorder) }.not_to change(recorder, :called_methods) end end context "stubbing with prepend", :if => Support::RubyFeatures.module_prepends_supported? do module ToBePrepended def value "#{super}_prepended".to_sym end end it "handles stubbing prepended methods" do klass = Class.new { prepend ToBePrepended; def value; :original; end } instance = klass.new expect(instance.value).to eq :original_prepended allow(instance).to receive(:value) { :stubbed } expect(instance.value).to eq :stubbed end it "handles stubbing prepended methods on a class's singleton class" do klass = Class.new { class << self; prepend ToBePrepended; end; def self.value; :original; end } expect(klass.value).to eq :original_prepended allow(klass).to receive(:value) { :stubbed } expect(klass.value).to eq :stubbed end it "handles stubbing prepended methods on an object's singleton class" do object = Object.new def object.value; :original; end object.singleton_class.send(:prepend, ToBePrepended) expect(object.value).to eq :original_prepended allow(object).to receive(:value) { :stubbed } expect(object.value).to eq :stubbed end it 'does not unnecessarily prepend a module when the prepended module does not override the stubbed method' do object = Object.new def object.value; :original; end object.singleton_class.send(:prepend, Module.new) expect { allow(object).to receive(:value) { :stubbed } }.not_to change { object.singleton_class.ancestors } end it 'does not unnecessarily prepend a module when stubbing a method on a module extended onto itself' do mod = Module.new do extend self def foo; :bar; end end expect { allow(mod).to receive(:foo) }.not_to change { mod.singleton_class.ancestors } end it 'does not unnecessarily prepend a module when the module was included' do object = Object.new def object.value; :original; end object.singleton_class.send(:include, ToBePrepended) expect { allow(object).to receive(:value) { :stubbed } }.not_to change { object.singleton_class.ancestors } end it 'reuses our prepend module so as not to keep mutating the ancestors' do object = Object.new def object.value; :original; end object.singleton_class.send(:prepend, ToBePrepended) allow(object).to receive(:value) { :stubbed } RSpec::Mocks.teardown RSpec::Mocks.setup expect { allow(object).to receive(:value) { :stubbed } }.not_to change { object.singleton_class.ancestors } end context "when multiple modules are prepended, only one of which overrides the stubbed method" do it "can still be stubbed and reset" do object = Object.new object.singleton_class.class_eval do def value; :original; end prepend ToBePrepended prepend Module.new {} end expect(object.value).to eq :original_prepended allow(object).to receive(:value) { :stubbed } expect(object.value).to eq :stubbed reset object expect(object.value).to eq :original_prepended end end context "when a module with a method override is prepended after reset" do it "can still be stubbed again" do object = Object.new def object.value; :original; end object.singleton_class.send(:prepend, ToBePrepended) allow(object).to receive(:value) { :stubbed } RSpec::Mocks.teardown RSpec::Mocks.setup object.singleton_class.send(:prepend, Module.new { def value :"#{super}_extra_prepend" end }) allow(object).to receive(:value) { :stubbed_2 } expect(object.value).to eq(:stubbed_2) end end end describe "#rspec_reset" do it "removes stubbed methods that didn't exist" do allow(@instance).to receive(:non_existent_method) reset @instance expect(@instance).not_to respond_to(:non_existent_method) end it "restores existing instance methods" do # See bug reports 8302 and 7805 allow(@instance).to receive(:existing_instance_method) { :stub_value } reset @instance expect(@instance.existing_instance_method).to eq(:original_value) end it "restores existing singleton methods with the appropriate context" do klass = Class.new do def self.say_hello @hello if defined?(@hello) end end subclass = Class.new(klass) subclass.instance_variable_set(:@hello, "Hello") expect(subclass.say_hello).to eq("Hello") allow(klass).to receive(:say_hello) { "Howdy" } expect(subclass.say_hello).to eq("Howdy") reset klass expect(subclass.say_hello).to eq("Hello") end it "restores existing private instance methods" do # See bug reports 8302 and 7805 allow(@instance).to receive(:existing_private_instance_method) { :stub_value } reset @instance expect(@instance.send(:existing_private_instance_method)).to eq(:original_value) end it "restores existing class methods" do # See bug reports 8302 and 7805 allow(@class).to receive(:existing_class_method) { :stub_value } reset @class expect(@class.existing_class_method).to eq(:original_value) end it "restores existing aliased module_function methods" do m = Module.new do def mkdir_p :mkdir_p end module_function :mkdir_p alias mkpath mkdir_p module_function :mkpath end allow(m).to receive(:mkpath) { :stub_value } allow(m).to receive(:mkdir_p) { :stub_value } reset m expect(m.mkpath).to eq(:mkdir_p) expect(m.mkdir_p).to eq(:mkdir_p) end it "restores existing private class methods" do # See bug reports 8302 and 7805 allow(@class).to receive(:existing_private_class_method) { :stub_value } reset @class expect(@class.send(:existing_private_class_method)).to eq(:original_value) end it "does not remove existing methods that have been stubbed twice" do allow(@instance).to receive(:existing_instance_method) allow(@instance).to receive(:existing_instance_method) reset @instance expect(@instance.existing_instance_method).to eq(:original_value) end it "correctly restores the visibility of methods whose visibility has been tweaked on the singleton class" do # hello is a private method when mixed in, but public on the module # itself mod = Module.new { extend self def hello; :hello; end private :hello class << self; public :hello; end; } expect(mod.hello).to eq(:hello) allow(mod).to receive(:hello) { :stub } reset mod expect(mod.hello).to eq(:hello) end it "correctly handles stubbing inherited mixed in class methods" do mod = Module.new do def method_a raise "should not execute method_a" end def self.included(other) other.extend self end end a = Class.new { include mod } b = Class.new(a) do def self.method_b "executed method_b" end end allow(a).to receive(:method_a) allow(b).to receive(:method_b).and_return("stubbed method_b") expect(b.method_b).to eql("stubbed method_b") end if Support::RubyFeatures.module_prepends_supported? context "with a prepended module (ruby 2.0.0+)" do module ToBePrepended def existing_method "#{super}_prepended".to_sym end end before do @prepended_class = Class.new do prepend ToBePrepended def existing_method :original_value end def non_prepended_method :not_prepended end end @prepended_instance = @prepended_class.new end it "restores prepended instance methods" do allow(@prepended_instance).to receive(:existing_method) { :stubbed } expect(@prepended_instance.existing_method).to eq :stubbed reset @prepended_instance expect(@prepended_instance.existing_method).to eq :original_value_prepended end it "restores non-prepended instance methods" do allow(@prepended_instance).to receive(:non_prepended_method) { :stubbed } expect(@prepended_instance.non_prepended_method).to eq :stubbed reset @prepended_instance expect(@prepended_instance.non_prepended_method).to eq :not_prepended end it "restores prepended class methods" do klass = Class.new do class << self; prepend ToBePrepended; end def self.existing_method :original_value end end allow(klass).to receive(:existing_method) { :stubbed } expect(klass.existing_method).to eq :stubbed reset klass expect(klass.existing_method).to eq :original_value_prepended end it "restores prepended object singleton methods" do object = Object.new def object.existing_method; :original_value; end object.singleton_class.send(:prepend, ToBePrepended) allow(object).to receive(:existing_method) { :stubbed } expect(object.existing_method).to eq :stubbed reset object expect(object.existing_method).to eq :original_value_prepended end end end end it "returns values in order to consecutive calls" do allow(@instance).to receive(:msg).and_return("1", 2, :three) expect(@instance.msg).to eq("1") expect(@instance.msg).to eq(2) expect(@instance.msg).to eq(:three) end it "keeps returning last value in consecutive calls" do allow(@instance).to receive(:msg).and_return("1", 2, :three) expect(@instance.msg).to eq("1") expect(@instance.msg).to eq(2) expect(@instance.msg).to eq(:three) expect(@instance.msg).to eq(:three) expect(@instance.msg).to eq(:three) end it "yields a specified object" do allow(@instance).to receive(:method_that_yields).and_yield(:yielded_obj) current_value = :value_before @instance.method_that_yields { |val| current_value = val } expect(current_value).to eq :yielded_obj verify @instance end it "yields multiple times with multiple calls to and_yield" do allow(@instance).to receive(:method_that_yields_multiple_times).and_yield(:yielded_value). and_yield(:another_value) current_value = [] @instance.method_that_yields_multiple_times { |val| current_value << val } expect(current_value).to eq [:yielded_value, :another_value] verify @instance end it "yields a specified object and return another specified object" do yielded_obj = double("my mock") expect(yielded_obj).to receive(:foo).with(:bar) allow(@instance).to receive(:method_that_yields_and_returns).and_yield(yielded_obj).and_return(:baz) expect(@instance.method_that_yields_and_returns { |o| o.foo :bar }).to eq :baz end it "throws when told to" do allow(@stub).to receive(:something).and_throw(:up) expect { @stub.something }.to throw_symbol(:up) end it "throws with argument when told to" do allow(@stub).to receive(:something).and_throw(:up, 'high') expect { @stub.something }.to throw_symbol(:up, 'high') end it "overrides a pre-existing method" do allow(@stub).to receive(:existing_instance_method).and_return(:updated_stub_value) expect(@stub.existing_instance_method).to eq :updated_stub_value end it "overrides a pre-existing stub" do allow(@stub).to receive(:foo) { 'bar' } allow(@stub).to receive(:foo) { 'baz' } expect(@stub.foo).to eq 'baz' end it "allows a stub and an expectation" do allow(@stub).to receive(:foo).with("bar") expect(@stub).to receive(:foo).with("baz") @stub.foo("bar") @stub.foo("baz") end end RSpec.describe "A method stub with args" do before(:each) do @stub = Object.new allow(@stub).to receive(:foo).with("bar") end it "does not complain if not called" do end it "does not complain if called with arg" do @stub.foo("bar") end it "complains if called with no arg" do expect { @stub.foo }.to raise_error(/received :foo with unexpected arguments/) end it "complains if called with other arg", :github_issue => [123, 147] do expect { @stub.foo("other") }.to raise_error(/received :foo with unexpected arguments.*Please stub a default value/m) end it "does not complain if also mocked w/ different args" do expect(@stub).to receive(:foo).with("baz") @stub.foo("bar") @stub.foo("baz") end it "complains if also mocked w/ different args AND called w/ a 3rd set of args" do expect(@stub).to receive(:foo).with("baz") @stub.foo("bar") @stub.foo("baz") expect { @stub.foo("other") }.to fail end it 'uses the correct stubbed response when responding to a mock expectation' do allow(@stub).to receive(:bar) { 15 } allow(@stub).to receive(:bar).with(:eighteen) { 18 } allow(@stub).to receive(:bar).with(:thirteen) { 13 } expect(@stub).to receive(:bar).exactly(4).times expect(@stub.bar(:blah)).to eq(15) expect(@stub.bar(:thirteen)).to eq(13) expect(@stub.bar(:eighteen)).to eq(18) expect(@stub.bar).to eq(15) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/stubbed_message_expectations_spec.rb000066400000000000000000000062021455767030500265030ustar00rootroot00000000000000RSpec.describe "expection set on previously stubbed method" do it "fails if message is not received after expectation is set" do dbl = double(:msg => nil) dbl.msg expect(dbl).to receive(:msg) expect { verify dbl }.to fail end it "outputs arguments of similar calls" do dbl = double('double', :foo => true) expect(dbl).to receive(:foo).with('first') dbl.foo('second') dbl.foo('third') expect { verify dbl }.to raise_error( RSpec::Mocks::MockExpectationError, a_string_including( %Q(# received :foo with unexpected arguments), "expected: (\"first\")", "got:", "(\"second\")", "(\"third\")")) reset dbl end it 'handles concurrent validation of expectations' do dbl = double('double', :foo => true) concurrency = 4 repetition = 10 expect(dbl).to receive(:foo).with(anything).exactly(concurrency * repetition).times concurrency.times.map do |thread| Thread.new do repetition.times do |index| dbl.foo("#{thread}-#{index}") end end end.map(&:join) verify dbl end it 'indicates the site of expectation in the stacktrace when outputing arguments of similar calls' do dbl = double('double', :foo => true) expect(dbl).to receive(:foo).with('first'); line = __LINE__ dbl.foo('second') dbl.foo('third') expect { verify dbl }.to raise_error(an_object_having_attributes( :backtrace => a_collection_starting_with( a_string_including("#{__FILE__}:#{line}") ) )) end context "with argument constraint on stub" do it "matches any args if no arg constraint set on expectation" do dbl = double("mock") allow(dbl).to receive(:foo).with(3).and_return("stub") expect(dbl).to receive(:foo).at_least(:once).and_return("expectation") dbl.foo verify dbl end it "matches specific args set on expectation" do dbl = double("mock") allow(dbl).to receive(:foo).with(3).and_return("stub") expect(dbl).to receive(:foo).at_least(:once).with(4).and_return("expectation") dbl.foo(4) verify dbl end it "fails if expectation's arg constraint is not met" do dbl = double("mock") allow(dbl).to receive(:foo).with(3).and_return("stub") expect(dbl).to receive(:foo).at_least(:once).with(4).and_return("expectation") dbl.foo(3) expect { verify dbl }.to raise_error(/expected: \(4\)\s+got: \(3\)/) end it 'distinguishes between individual values and arrays properly' do dbl = double allow(dbl).to receive(:foo).with('a', ['b']) expect { dbl.foo(['a'], 'b') }.to raise_error { |e| expect(e.message).to include('expected: ("a", ["b"])', 'got: (["a"], "b")') } end it 'distinguishes between duplicate individual values and arrays properly' do dbl = double allow(dbl).to receive(:foo).with('a', ['b'], 'b') expect { dbl.foo(['a'], 'b', 'b') }.to raise_error { |e| expect(e.message).to include('expected: ("a", ["b"], "b")', 'got: (["a"], "b", "b")') } end end end rspec-mocks-3.13.0/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb000066400000000000000000000064131455767030500273740ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe ".allow_message" do let(:subject) { Object.new } it "sets up basic message allowance" do expect { ::RSpec::Mocks.allow_message(subject, :basic) }.to change { subject.respond_to?(:basic) }.to(true) expect(subject.basic).to eq(nil) end it "sets up message allowance with params and return value" do expect { ::RSpec::Mocks.allow_message(subject, :x).with(:in).and_return(:out) }.to change { subject.respond_to?(:x) }.to(true) expect(subject.x(:in)).to eq(:out) end it "supports block implementations" do ::RSpec::Mocks.allow_message(subject, :message) { :value } expect(subject.message).to eq(:value) end it "does not set an expectation that the message will be received" do ::RSpec::Mocks.allow_message(subject, :message) expect { verify subject }.not_to raise_error end it 'does not get confused when the string and symbol message form are both used' do ::RSpec::Mocks.allow_message(subject, :foo).with(1) { :a } ::RSpec::Mocks.allow_message(subject, "foo").with(2) { :b } expect(subject.foo(1)).to eq(:a) expect(subject.foo(2)).to eq(:b) reset subject end context 'when target cannot be proxied' do it 'raises ArgumentError with message' do expect { ::RSpec::Mocks.allow_message(:subject, :foo) { :a } }.to raise_error(ArgumentError) end end end RSpec.describe ".expect_message" do let(:subject) { Object.new } it "sets up basic message expectation, verifies as uncalled" do expect { ::RSpec::Mocks.expect_message(subject, :basic) }.to change { subject.respond_to?(:basic) }.to(true) expect { verify subject }.to fail end it "fails if never is specified and the message is called" do expect_fast_failure_from(subject, /expected.*0 times/) do ::RSpec::Mocks.expect_message(subject, :foo).never subject.foo end end it "sets up basic message expectation, verifies as called" do ::RSpec::Mocks.expect_message(subject, :basic) subject.basic verify subject end it "sets up message expectation with params and return value" do ::RSpec::Mocks.expect_message(subject, :msg).with(:in).and_return(:out) expect(subject.msg(:in)).to eq(:out) verify subject end it "accepts a block implementation for the expected message" do ::RSpec::Mocks.expect_message(subject, :msg) { :value } expect(subject.msg).to eq(:value) verify subject end it 'does not get confused when the string and symbol message form are both used' do ::RSpec::Mocks.expect_message(subject, :foo).with(1) ::RSpec::Mocks.expect_message(subject, "foo").with(2) subject.foo(1) subject.foo(2) verify subject end context 'when target cannot be proxied' do it 'raises ArgumentError with message' do expect { ::RSpec::Mocks.expect_message(:subject, :foo) { :a } }.to raise_error(ArgumentError) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/syntax_spec.rb000066400000000000000000000003061455767030500221060ustar00rootroot00000000000000module RSpec RSpec.describe Mocks do it "does not inadvertently define BasicObject on 1.8", :if => RUBY_VERSION.to_f < 1.9 do expect(defined?(::BasicObject)).to be nil end end end rspec-mocks-3.13.0/spec/rspec/mocks/test_double_spec.rb000066400000000000000000000032021455767030500230670ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe TestDouble do describe "#freeze" do subject { double } it "gives a warning" do expect(RSpec).to receive(:warn_with).with(/freeze a test double/) subject.freeze end it "gives the correct call site for the warning" do expect_warning_with_call_site(__FILE__, __LINE__ + 1) subject.freeze end it "doesn't freeze the object" do allow(RSpec).to receive(:warn_with).with(/freeze a test double/) double.freeze allow(subject).to receive(:hi) expect { subject.hi }.not_to raise_error end it "returns the instance of the test double" do allow(RSpec).to receive(:warn_with).with(/freeze a test double/) expect(subject.freeze).to eq subject end end RSpec.shared_examples_for "a copy method" do |method| it "copies the `as_null_object` state when #{method}'d" do dbl = double.as_null_object copy = dbl.__send__(method) expect(copy.foo.bar).to be(copy) end end include_examples "a copy method", :dup include_examples "a copy method", :clone [[:should, :expect], [:expect], [:should]].each do |syntax| context "with syntax #{syntax.inspect}" do include_context "with syntax", syntax it 'stubs the methods passed in the stubs hash' do dbl = double("MyDouble", :a => 5, :b => 10) expect(dbl.a).to eq(5) expect(dbl.b).to eq(10) end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/thrice_counts_spec.rb000066400000000000000000000045321455767030500234360ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "#thrice" do before(:each) do @double = double("test double") end it "passes when called thrice" do expect(@double).to receive(:do_something).thrice 3.times { @double.do_something } verify @double end it "passes when called thrice with specified args" do expect(@double).to receive(:do_something).thrice.with("1", 1) 3.times { @double.do_something("1", 1) } verify @double end it "passes when called thrice with unspecified args" do expect(@double).to receive(:do_something).thrice @double.do_something("1") @double.do_something(1) @double.do_something(nil) verify @double end it "fails fast when call count is higher than expected" do expect(@double).to receive(:do_something).thrice 3.times { @double.do_something } expect_fast_failure_from(@double) do @double.do_something end end it "fails when call count is lower than expected" do expect(@double).to receive(:do_something).thrice @double.do_something expect { verify @double }.to fail end it "fails when called with wrong args on the first call" do expect(@double).to receive(:do_something).thrice.with("1", 1) expect { @double.do_something(1, "1") }.to fail reset @double end it "fails when called with wrong args on the second call" do expect(@double).to receive(:do_something).thrice.with("1", 1) @double.do_something("1", 1) expect { @double.do_something(1, "1") }.to fail reset @double end it "fails when called with wrong args on the third call" do expect(@double).to receive(:do_something).thrice.with("1", 1) @double.do_something("1", 1) @double.do_something("1", 1) expect { @double.do_something(1, "1") }.to fail reset @double end context "when called with negative expectation" do it "raises an error" do expect { expect(@double).not_to receive(:do_something).thrice }.to raise_error(/`count` is not supported with negative message expectations/) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/to_ary_spec.rb000066400000000000000000000023571455767030500220650ustar00rootroot00000000000000RSpec.describe "a double receiving to_ary" do shared_examples "to_ary" do it "can be overridden with a stub" do allow(obj).to receive(:to_ary) { :non_nil_value } expect(obj.to_ary).to be(:non_nil_value) end it "responds when overridden" do allow(obj).to receive(:to_ary) { :non_nil_value } expect(obj).to respond_to(:to_ary) end it "supports Array#flatten" do dbl = double('foo') expect([dbl].flatten).to eq([dbl]) end end context "double as_null_object" do let(:obj) { double('obj').as_null_object } include_examples "to_ary" it "does respond to to_ary" do expect(obj).to respond_to(:to_ary) end it "does respond to to_a" do expect(obj).to respond_to(:to_a) end it "returns nil" do expect(obj.to_ary).to eq nil end end context "double without as_null_object" do let(:obj) { double('obj') } include_examples "to_ary" it "doesn't respond to to_ary" do expect(obj).not_to respond_to(:to_ary) end it "doesn't respond to to_a", :if => (RUBY_VERSION.to_f > 1.8) do expect(obj).not_to respond_to(:to_a) end it "raises " do expect { obj.to_ary }.to raise_error(NoMethodError) end end end rspec-mocks-3.13.0/spec/rspec/mocks/twice_counts_spec.rb000066400000000000000000000065041455767030500232740ustar00rootroot00000000000000module RSpec module Mocks RSpec.describe "#twice" do before(:each) do @double = double("test double") end it "passes when called twice" do expect(@double).to receive(:do_something).twice @double.do_something @double.do_something verify @double end it "passes when called twice with specified args" do expect(@double).to receive(:do_something).twice.with("1", 1) @double.do_something("1", 1) @double.do_something("1", 1) verify @double end it "passes when called twice with unspecified args" do expect(@double).to receive(:do_something).twice @double.do_something("1") @double.do_something(1) verify @double end it "fails fast when call count is higher than expected" do expect(@double).to receive(:do_something).twice @double.do_something @double.do_something expect_fast_failure_from(@double) do @double.do_something end end it "fails when call count is lower than expected" do expect(@double).to receive(:do_something).twice @double.do_something expect { verify @double }.to fail end it "fails when called with wrong args on the first call" do expect(@double).to receive(:do_something).twice.with("1", 1) expect { @double.do_something(1, "1") }.to fail reset @double end it "fails when called with wrong args on the second call" do expect(@double).to receive(:do_something).twice.with("1", 1) @double.do_something("1", 1) expect { @double.do_something(1, "1") }.to fail reset @double end context "when called with the wrong number of times with the specified args and also called with different args" do it "mentions the wrong call count in the failure message rather than the different args" do allow(@double).to receive(:do_something) # allow any args... expect(@double).to receive(:do_something).with(:args, 1).twice @double.do_something(:args, 2) @double.do_something(:args, 1) @double.do_something(:args, 2) @double.do_something(:args, 1) expect { # we've grouped these lines because it should probably fail fast # on the first line (since our expectation above only allows one # call with these args), but currently it fails with a confusing # message on verification, and ultimately we care more about # what the message is than when it is raised. Still, it would be # preferrable for the error to be triggered on the first line, # so it'd be good to update this spec to enforce that once we # get the failure message right. @double.do_something(:args, 1) verify @double }.to fail_with(a_string_including("expected: 2 times", "received: 3 times")) end end context "when called with negative expectation" do it "raises an error" do expect { expect(@double).not_to receive(:do_something).twice }.to raise_error(/`count` is not supported with negative message expectations/) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/000077500000000000000000000000001455767030500227415ustar00rootroot00000000000000rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/class_double_with_class_loaded_spec.rb000066400000000000000000000176611455767030500325020ustar00rootroot00000000000000require 'support/doubled_classes' module RSpec module Mocks RSpec.describe 'A class double with the doubled class loaded' do include_context "with isolated configuration" before do RSpec::Mocks.configuration.verify_doubled_constant_names = true end it 'only allows class methods that exist to be stubbed' do o = class_double('LoadedClass', :defined_class_method => 1) expect(o.defined_class_method).to eq(1) prevents { allow(o).to receive(:undefined_instance_method) } prevents { allow(o).to receive(:defined_instance_method) } end it 'only allows class methods that exist to be expected' do o = class_double('LoadedClass') expect(o).to receive(:defined_class_method) o.defined_class_method prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_instance_method) } prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_instance_method) } end USE_INSTANCE_DOUBLE_MSG = "Perhaps you meant to use `instance_double`" it "suggests using `instance_double` when an instance method is stubbed" do o = class_double("LoadedClass") prevents(a_string_including(USE_INSTANCE_DOUBLE_MSG)) { allow(o).to receive(:defined_instance_method) } end it "doesn't suggest `instance_double` when a non-instance method is stubbed'" do o = class_double("LoadedClass") prevents(a_string_excluding(USE_INSTANCE_DOUBLE_MSG)) { allow(o).to receive(:undefined_instance_method) } end it 'gives a descriptive error message for NoMethodError' do o = class_double("LoadedClass") expect { o.defined_private_class_method }.to raise_error(NoMethodError, a_string_including("ClassDouble(LoadedClass)")) end it 'checks that stubbed methods are invoked with the correct arity' do o = class_double('LoadedClass', :defined_class_method => 1) expect { o.defined_class_method(:a) }.to raise_error(ArgumentError) end it 'allows dynamically defined class method stubs with arguments' do o = class_double('LoadedClass') allow(o).to receive(:dynamic_class_method).with(:a) { 1 } expect(o.dynamic_class_method(:a)).to eq(1) end it 'allows dynamically defined class method mocks with arguments' do o = class_double('LoadedClass') expect(o).to receive(:dynamic_class_method).with(:a) o.dynamic_class_method(:a) end it 'allows dynamically defined class methods to be expected' do o = class_double('LoadedClass', :dynamic_class_method => 1) expect(o.dynamic_class_method).to eq(1) end it 'allows class to be specified by constant' do o = class_double(LoadedClass, :defined_class_method => 1) expect(o.defined_class_method).to eq(1) end it 'can replace existing constants for the duration of the test' do original = LoadedClass object = class_double('LoadedClass').as_stubbed_const expect(object).to receive(:defined_class_method) expect(LoadedClass).to eq(object) ::RSpec::Mocks.teardown ::RSpec::Mocks.setup expect(LoadedClass).to eq(original) end it 'can transfer nested constants to the double' do class_double("LoadedClass"). as_stubbed_const(:transfer_nested_constants => true) expect(LoadedClass::M).to eq(:m) expect(LoadedClass::N).to eq(:n) end it 'correctly verifies expectations when constant is removed' do dbl1 = class_double(LoadedClass::Nested).as_stubbed_const class_double(LoadedClass).as_stubbed_const prevents { expect(dbl1).to receive(:undefined_class_method) } end it 'only allows defined methods for null objects' do o = class_double('LoadedClass').as_null_object expect(o.defined_class_method).to eq(o) prevents { o.undefined_method } end it 'verifies arguments for null objects' do o = class_double('LoadedClass').as_null_object expect { o.defined_class_method(:too, :many, :args) }.to raise_error(ArgumentError, "Wrong number of arguments. Expected 0, got 3.") end it 'validates `with` args against the method signature when stubbing a method' do dbl = class_double(LoadedClass) prevents(/Wrong number of arguments. Expected 0, got 2./) { allow(dbl).to receive(:defined_class_method).with(2, :args) } end context "when `.new` is stubbed" do before do expect(LoadedClass.instance_method(:initialize).arity).to eq(2) end it 'uses the method signature from `#initialize` for arg verification' do o = class_double(LoadedClass) prevents(/arguments/) { allow(o).to receive(:new).with(1) } allow(o).to receive(:new).with(1, 2) end context "on a class that has redefined `new`" do it "uses the method signature of the redefined `new` for arg verification" do klass = Class.new(LoadedClass) do def self.new(_); end end o = class_double(klass) prevents(/arguments/) { allow(o).to receive(:new).with(1, 2) } allow(o).to receive(:new).with(1) end end context "on a class that has undefined `new`" do it "prevents it from being stubbed" do klass = Class.new(LoadedClass) do class << self undef new end end o = class_double(klass) prevents(/does not implement the class method/) { allow(o).to receive(:new).with(1, 2) } end end context "on a class with a private `new`" do it 'uses the method signature from `#initialize` for arg verification' do if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version < '9.2.1.0' pending "Failing on JRuby due to https://github.com/jruby/jruby/issues/2565" end klass = Class.new(LoadedClass) do private_class_method :new end o = class_double(klass) prevents(/arguments/) { allow(o).to receive(:new).with(1) } allow(o).to receive(:new).with(1, 2) end end end context "when given an anonymous class" do it 'properly verifies' do subclass = Class.new(LoadedClass) o = class_double(subclass) allow(o).to receive(:defined_class_method) prevents { allow(o).to receive(:undefined_method) } end end context "when given a class that has an overridden `#name` method" do it "properly verifies" do check_verification class_double(LoadedClassWithOverriddenName) end it "can still stub the const" do class_double(LoadedClassWithOverriddenName).as_stubbed_const check_verification LoadedClassWithOverriddenName end def check_verification(o) allow(o).to receive(:defined_class_method) prevents { allow(o).to receive(:undefined_method) } end end context "when the class const has been previously stubbed" do before { stub_const("LoadedClass", Class.new) } it "uses the original class to verify against for `class_double('ClassName')`" do o = class_double("LoadedClass") allow(o).to receive(:defined_class_method) prevents { allow(o).to receive(:undefined_method) } end it "uses the original class to verify against for `instance_double(ClassName)`" do o = class_double(LoadedClass) allow(o).to receive(:defined_class_method) prevents { allow(o).to receive(:undefined_method) } end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/class_double_with_class_not_loaded_spec.rb000066400000000000000000000044171455767030500333550ustar00rootroot00000000000000require 'support/doubled_classes' module RSpec module Mocks RSpec.describe 'A class double with the doubled class not loaded' do include_context "with isolated configuration" before do RSpec::Mocks.configuration.verify_doubled_constant_names = false end it 'includes the double name in errors for unexpected messages' do o = class_double("NonLoadedClass") expect { o.foo }.to fail_including('ClassDouble(NonLoadedClass)') end it 'allows any method to be stubbed' do o = class_double('NonloadedClass') allow(o).to receive(:undefined_instance_method).with(:arg).and_return(1) expect(o.undefined_instance_method(:arg)).to eq(1) end specify "trying to raise a class_double raises a TypeError", :unless => RUBY_VERSION == '1.9.2' do subject = Object.new class_double("StubbedError").as_stubbed_const allow(subject).to receive(:some_method).and_raise(StubbedError) expect { subject.some_method }.to raise_error(TypeError, 'exception class/object expected') end context "when stubbing a private module method" do before(:all) do Module.class_exec do private def use; end end end after(:all) do Module.class_exec do undef use end end it 'can mock private module methods' do double = Module.new allow(double).to receive(:use) expect { double.use }.to raise_error(/private method `use' called/) double = class_double("NonloadedClass") expect(double).to receive(:use).and_return(:ok) expect(double.use).to be(:ok) end end context "when the class const has been previously stubbed" do before { stub_const("NonLoadedClass", Class.new) } it "treats the class as being unloaded for `class_double('NonLoadedClass')`" do o = class_double("NonLoadedClass") allow(o).to receive(:undefined_method) end it "treats the class as being unloaded for `instance_double(NonLoadedClass)`" do o = class_double(NonLoadedClass) allow(o).to receive(:undefined_method) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/construction_spec.rb000066400000000000000000000141761455767030500270430ustar00rootroot00000000000000require 'support/doubled_classes' module RSpec module Mocks RSpec.describe 'Constructing a verifying double' do include_context 'with isolated configuration' class ClassThatDynamicallyDefinesMethods def self.define_attribute_methods! define_method(:some_method_defined_dynamically) { true } end end module CustomModule end describe 'instance doubles' do it 'cannot be constructed with a non-module object' do expect { instance_double(Object.new) }.to raise_error(/Module or String expected/) end it 'can be constructed with a struct' do o = instance_double(Struct.new(:defined_method), :defined_method => 1) expect(o.defined_method).to eq(1) end it 'allows named constants to be looked up and declared to verifying double callbacks' do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.verify_doubled_constant_names = true config.when_declaring_verifying_double(&probe) end instance_double("RSpec::Mocks::ClassThatDynamicallyDefinesMethods") }.to yield_with_args(have_attributes :target => ClassThatDynamicallyDefinesMethods) end it 'allows anonymous constants to be looked up and declared to verifying double callbacks' do anonymous_module = Module.new expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.verify_doubled_constant_names = true config.when_declaring_verifying_double(&probe) end instance_double(anonymous_module) }.to yield_with_args(have_attributes :target => anonymous_module) end it 'allows classes to be customised' do test_class = Class.new(ClassThatDynamicallyDefinesMethods) RSpec.configuration.mock_with(:rspec) do |config| config.when_declaring_verifying_double do |reference| reference.target.define_attribute_methods! end end instance_double(test_class, :some_method_defined_dynamically => true) end describe 'any_instance' do let(:test_class) { Class.new(ClassThatDynamicallyDefinesMethods) } let(:not_implemented_error) { "#{test_class} does not implement #some_invalid_method" } before(:each) do RSpec.configuration.mock_with(:rspec) do |config| config.verify_partial_doubles = true config.when_declaring_verifying_double do |reference| reference.target.define_attribute_methods! if reference.target == test_class end end end it 'calls the callback for expect_any_instance_of' do expect_any_instance_of(test_class).to receive(:some_method_defined_dynamically) expect { expect_any_instance_of(test_class).to receive(:some_invalid_method) }.to raise_error(RSpec::Mocks::MockExpectationError, not_implemented_error) expect(test_class.new.some_method_defined_dynamically).to eq(nil) end it 'calls the callback for allow_any_instance_of' do allow_any_instance_of(test_class).to receive(:some_method_defined_dynamically) expect { allow_any_instance_of(test_class).to receive(:some_invalid_method) }.to raise_error(RSpec::Mocks::MockExpectationError, not_implemented_error) expect(test_class.new.some_method_defined_dynamically).to eq(nil) end it 'should not call the callback if verify_partial_doubles is off' do RSpec.configuration.mock_with(:rspec) do |config| config.verify_partial_doubles = false end expect(test_class.method_defined?(:some_method_defined_dynamically)).to be_falsey end end end describe 'class doubles' do it 'cannot be constructed with a non-module object' do expect { class_double(Object.new) }.to raise_error(/Module or String expected/) end it 'declares named modules to verifying double callbacks' do expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.when_declaring_verifying_double(&probe) end class_double CustomModule }.to yield_with_args(have_attributes :target => CustomModule) end it 'declares anonymous modules to verifying double callbacks' do anonymous_module = Module.new expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.when_declaring_verifying_double(&probe) end class_double anonymous_module }.to yield_with_args(have_attributes :target => anonymous_module) end end describe 'object doubles' do it 'declares the class to verifying double callbacks' do object = Object.new expect { |probe| RSpec.configuration.mock_with(:rspec) do |config| config.when_declaring_verifying_double(&probe) end object_double object }.to yield_with_args(have_attributes :target => object) end end describe 'when verify_doubled_constant_names config option is set' do before do RSpec::Mocks.configuration.verify_doubled_constant_names = true end it 'prevents creation of instance doubles for unloaded constants' do expect { instance_double('LoadedClas') }.to raise_error(VerifyingDoubleNotDefinedError) end it 'prevents creation of class doubles for unloaded constants' do expect { class_double('LoadedClas') }.to raise_error(VerifyingDoubleNotDefinedError) end end it 'can only be named with a string or a module' do expect { instance_double(1) }.to raise_error(ArgumentError) expect { instance_double(nil) }.to raise_error(ArgumentError) end end end end rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/expected_arg_verification_spec.rb000066400000000000000000000151721455767030500315020ustar00rootroot00000000000000require 'support/doubled_classes' module RSpec module Mocks RSpec.describe 'Expected argument verification (when `#with` is called)' do # Note: these specs here aren't meant to be exhaustive. The specs in # rspec-support for the method signature verifier are. Here we are just # covering the code paths within the `with` implementation, including # the special handling for `any_args` and `no_args`. context "when doubling an unloaded class" do it 'allows any arguments' do expect(defined?(UnloadedClass)).to be_falsey inst_dbl = instance_double("UnloadedClass") expect { expect(inst_dbl).to receive(:message).with(:foo, :bar, :bazz) }.not_to raise_error reset inst_dbl end end context "when doubling a loaded class" do let(:dbl) { instance_double(LoadedClass) } after { reset dbl } context "when `any_args` is used" do context "as the only argument" do it "is allowed regardless of how many args the method requires" do expect { expect(dbl).to receive(:instance_method_with_two_args).with(any_args) }.not_to raise_error end end context "as the first argument, with too many additional args" do it "is disallowed" do expect { expect(dbl).to receive(:instance_method_with_two_args).with(any_args, 1, 2, 3) }.to fail_with("Wrong number of arguments. Expected 2, got 3.") end end context "as the first argument, with an allowed number of additional args" do it "is allowed" do expect { expect(dbl).to receive(:instance_method_with_two_args).with(any_args, 1, 2) }.not_to raise_error expect { expect(dbl).to receive(:instance_method_with_two_args).with(any_args, 1) }.not_to raise_error end end end context "when `no_args` is used" do it "allows a method expectation on a method that accepts no arguments" do expect(LoadedClass.instance_method(:defined_instance_method).arity).to eq(0) expect { expect(dbl).to receive(:defined_instance_method).with(no_args) }.not_to raise_error end it "allows a method expectation on a method that has defaults for all arguments" do expect { expect(dbl).to receive(:instance_method_with_only_defaults).with(no_args) }.not_to raise_error end it "does not allow a method expectation on a method that has required arguments" do expect { expect(dbl).to receive(:instance_method_with_two_args).with(no_args) }.to fail_with("Wrong number of arguments. Expected 2, got 0.") end if RSpec::Support::RubyFeatures.required_kw_args_supported? context "for a method with required keyword args" do it 'covers the required args when `any_args` is last' do expect { expect(dbl).to receive(:kw_args_method).with(1, any_args) }.not_to raise_error end it 'does not cover the required args when there are args after `any_args`' do expect { # Use eval to avoid syntax error on 1.8 and 1.9 eval("expect(dbl).to receive(:kw_args_method).with(any_args, optional_arg: 3)") }.to fail_with("Missing required keyword arguments: required_arg") end end end end if RSpec::Support::RubyFeatures.required_kw_args_supported? it 'does not cover required args when `any_args` is not used' do expect { expect(dbl).to receive(:kw_args_method).with(anything) }.to fail_with("Missing required keyword arguments: required_arg") end end context "when a list of args is provided" do it "allows a method expectation when the arity matches" do expect { expect(dbl).to receive(:instance_method_with_two_args).with(1, 2) }.not_to raise_error end it "does not allow a method expectation with an arity mismatch" do expect { expect(dbl).to receive(:instance_method_with_two_args).with(1, 2, 3) }.to fail_with("Wrong number of arguments. Expected 2, got 3.") end end context "when `with` is called with no args" do it "fails with an error suggesting the user use `no_args` instead" do expect { expect(dbl).to receive(:instance_method_with_two_args).with }.to raise_error(ArgumentError, /no_args/) end end if RSpec::Support::RubyFeatures.required_kw_args_supported? context "for a method with keyword args" do it "matches against a hash submitted as keyword arguments and received as positional argument (in both Ruby 2 and Ruby 3)" do expect(dbl).to receive(:kw_args_method).with(1, {:required_arg => 2, :optional_arg => 3}) dbl.kw_args_method(1, :required_arg => 2, :optional_arg => 3) end if RUBY_VERSION >= "3" it "fails to match against a hash submitted as a positional argument and received as keyword arguments in Ruby 3.0 or later", :reset => true do expect(dbl).to receive(:kw_args_method).with(1, :required_arg => 2, :optional_arg => 3) expect do dbl.kw_args_method(1, {:required_arg => 2, :optional_arg => 3}) end.to fail_with(a_string_including("expected: (1, {:optional_arg=>3, :required_arg=>2}) (keyword arguments)", "got: (1, {:optional_arg=>3, :required_arg=>2}) (options hash)")) end else it "matches against a hash submitted as a positional argument and received as keyword arguments in Ruby 2.7 or before" do expect(dbl).to receive(:kw_args_method).with(1, :required_arg => 2, :optional_arg => 3) dbl.kw_args_method(1, {:required_arg => 2, :optional_arg => 3}) end end context "when using `send`" do let(:dbl) { instance_double(Class.new { eval "def m(k:); end" }) } it "matches against keyword arguments" do expect(dbl).to receive(:m).with(:k => 1) dbl.send(:m, :k => 1) end end end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/instance_double_with_class_loaded_spec.rb000066400000000000000000000227471455767030500332020ustar00rootroot00000000000000require 'support/doubled_classes' module RSpec module Mocks RSpec.describe 'An instance double with the doubled class loaded' do include_context "with isolated configuration" before do RSpec::Mocks.configuration.verify_doubled_constant_names = true end it 'only allows instance methods that exist to be stubbed' do o = instance_double('LoadedClass', :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) prevents(/does not implement the instance method/) { allow(o).to receive(:undefined_instance_method) } prevents(/does not implement the instance method/) { allow(o).to receive(:defined_class_method) } end it 'only allows instance methods that exist to be expected' do o = instance_double('LoadedClass') expect(o).to receive(:defined_instance_method) o.defined_instance_method prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_class_method) } prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_class_method) } end USE_CLASS_DOUBLE_MSG = "Perhaps you meant to use `class_double`" it "suggests using `class_double` when a class method is stubbed" do o = instance_double("LoadedClass") prevents(a_string_including(USE_CLASS_DOUBLE_MSG)) { allow(o).to receive(:defined_class_method) } end it "doesn't suggest `class_double` when a non-class method is stubbed" do o = instance_double("LoadedClass") prevents(a_string_excluding(USE_CLASS_DOUBLE_MSG)) { allow(o).to receive(:undefined_class_method) } end it 'allows `send` to be stubbed if it is defined on the class' do o = instance_double('LoadedClass') allow(o).to receive(:send).and_return("received") expect(o.send(:msg)).to eq("received") end it 'gives a descriptive error message for NoMethodError' do o = instance_double("LoadedClass") expect { o.defined_private_method }.to raise_error(NoMethodError, a_string_including("InstanceDouble(LoadedClass)")) end it 'does not allow dynamic methods to be expected' do # This isn't possible at the moment since an instance of the class # would be required for the verification, and we only have the # class itself. # # This spec exists as "negative" documentation of the absence of a # feature, to highlight the asymmetry from class doubles (that do # support this behaviour). prevents { instance_double('LoadedClass', :dynamic_instance_method => 1) } end it 'checks the arity of stubbed methods' do o = instance_double('LoadedClass') prevents { expect(o).to receive(:defined_instance_method).with(:a) } reset o end it 'checks that stubbed methods are invoked with the correct arity' do o = instance_double('LoadedClass', :defined_instance_method => 25) expect { o.defined_instance_method(:a) }.to raise_error(ArgumentError, "Wrong number of arguments. Expected 0, got 1.") end if required_kw_args_supported? it 'allows keyword arguments' do o = instance_double('LoadedClass', :kw_args_method => true) expect(o.kw_args_method(1, :required_arg => 'something')).to eq(true) end context 'for a method that only accepts keyword args' do it 'allows hash matchers like `hash_including` to be used in place of the keywords arg hash' do o = instance_double('LoadedClass') expect(o).to receive(:kw_args_method). with(1, hash_including(:required_arg => 1)) o.kw_args_method(1, :required_arg => 1) end it 'allows anything matcher to be used in place of the keywords arg hash' do o = instance_double('LoadedClass') expect(o).to receive(:kw_args_method).with(1, anything) o.kw_args_method(1, :required_arg => 1) end it 'still checks positional arguments when matchers used for keyword args' do o = instance_double('LoadedClass') prevents(/Expected 1, got 3/) { expect(o).to receive(:kw_args_method). with(1, 2, 3, hash_including(:required_arg => 1)) } reset o end it 'does not allow matchers to be used in an actual method call' do o = instance_double('LoadedClass') matcher = hash_including(:required_arg => 1) allow(o).to receive(:kw_args_method).with(1, matcher) expect { o.kw_args_method(1, matcher) }.to raise_error(ArgumentError) end end context 'for a method that accepts a mix of optional keyword and positional args' do it 'allows hash matchers like `hash_including` to be used in place of the keywords arg hash' do o = instance_double('LoadedClass') expect(o).to receive(:mixed_args_method).with(1, 2, hash_including(:optional_arg_1 => 1)) o.mixed_args_method(1, 2, :optional_arg_1 => 1) end end it 'checks that stubbed methods with required keyword args are ' \ 'invoked with the required arguments' do o = instance_double('LoadedClass', :kw_args_method => true) expect { o.kw_args_method(:optional_arg => 'something') }.to raise_error(ArgumentError) end end it 'validates `with` args against the method signature when stubbing a method' do dbl = instance_double(LoadedClass) prevents(/Wrong number of arguments. Expected 2, got 3./) { allow(dbl).to receive(:instance_method_with_two_args).with(3, :foo, :args) } end it 'allows class to be specified by constant' do o = instance_double(LoadedClass, :defined_instance_method => 1) expect(o.defined_instance_method).to eq(1) end context "when the class const has been previously stubbed" do before { class_double(LoadedClass).as_stubbed_const } it "uses the original class to verify against for `instance_double('LoadedClass')`" do o = instance_double("LoadedClass") allow(o).to receive(:defined_instance_method) prevents { allow(o).to receive(:undefined_method) } end it "uses the original class to verify against for `instance_double(LoadedClass)`" do o = instance_double(LoadedClass) allow(o).to receive(:defined_instance_method) prevents { allow(o).to receive(:undefined_method) } end end context "when given a class that has an overridden `#name` method" do it "properly verifies" do o = instance_double(LoadedClassWithOverriddenName) allow(o).to receive(:defined_instance_method) prevents { allow(o).to receive(:undefined_method) } end end context 'for null objects' do let(:obj) { instance_double('LoadedClass').as_null_object } it 'only allows defined methods' do expect(obj.defined_instance_method).to eq(obj) prevents { obj.undefined_method } prevents { obj.send(:undefined_method) } prevents { obj.__send__(:undefined_method) } end it 'verifies arguments' do expect { obj.defined_instance_method(:too, :many, :args) }.to raise_error(ArgumentError, "Wrong number of arguments. Expected 0, got 3.") end it "includes the double's name in a private method error" do expect { obj.rand }.to raise_error(NoMethodError, a_string_including("private", "InstanceDouble(LoadedClass)")) end it 'reports what public messages it responds to accurately' do expect(obj.respond_to?(:defined_instance_method)).to be true expect(obj.respond_to?(:defined_instance_method, true)).to be true expect(obj.respond_to?(:defined_instance_method, false)).to be true expect(obj.respond_to?(:undefined_method)).to be false expect(obj.respond_to?(:undefined_method, true)).to be false expect(obj.respond_to?(:undefined_method, false)).to be false end it 'reports that it responds to defined private methods when the appropriate arg is passed' do expect(obj.respond_to?(:defined_private_method)).to be false expect(obj.respond_to?(:defined_private_method, true)).to be true expect(obj.respond_to?(:defined_private_method, false)).to be false end if RUBY_VERSION.to_f < 2.0 # respond_to?(:protected_method) changed behavior in Ruby 2.0. it 'reports that it responds to protected methods' do expect(obj.respond_to?(:defined_protected_method)).to be true expect(obj.respond_to?(:defined_protected_method, true)).to be true expect(obj.respond_to?(:defined_protected_method, false)).to be true end else it 'reports that it responds to protected methods when the appropriate arg is passed' do expect(obj.respond_to?(:defined_protected_method)).to be false expect(obj.respond_to?(:defined_protected_method, true)).to be true expect(obj.respond_to?(:defined_protected_method, false)).to be false end end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/instance_double_with_class_not_loaded_spec.rb000066400000000000000000000044161455767030500340530ustar00rootroot00000000000000require 'support/doubled_classes' module RSpec module Mocks RSpec.describe 'An instance double with the doubled class not loaded' do include_context "with isolated configuration" before do RSpec::Mocks.configuration.verify_doubled_constant_names = false end it 'includes the doubled module in errors for unexpected messages' do o = instance_double("NonLoadedClass") expect { o.foo }.to fail_including('InstanceDouble(NonLoadedClass)') end it 'allows any instance method to be stubbed' do o = instance_double('NonloadedClass') allow(o).to receive(:undefined_instance_method).with(:arg).and_return(true) expect(o.undefined_instance_method(:arg)).to eq(true) end it 'allows any instance method to be expected' do o = instance_double("NonloadedClass") expect(o).to receive(:undefined_instance_method). with(:arg). and_return(true) expect(o.undefined_instance_method(:arg)).to eq(true) end it 'handles classes that are materialized after mocking' do stub_const "A::B", Object.new o = instance_double "A", :undefined_instance_method => true expect(o.undefined_instance_method).to eq(true) end context 'for null objects' do let(:obj) { instance_double('NonLoadedClass').as_null_object } it 'returns self from any message' do expect(obj.a.b.c).to be(obj) end it 'reports it responds to any message' do expect(obj.respond_to?(:a)).to be true expect(obj.respond_to?(:a, false)).to be true expect(obj.respond_to?(:a, true)).to be true end end context "when the class const has been previously stubbed" do before { class_double("NonLoadedClass").as_stubbed_const } it "treats the class as unloaded for `instance_double('NonLoadedClass')`" do o = instance_double("NonLoadedClass") allow(o).to receive(:undefined_method) end it "treats the class as unloaded for `instance_double(NonLoadedClass)`" do o = instance_double(NonLoadedClass) allow(o).to receive(:undefined_method) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/method_visibility_spec.rb000066400000000000000000000114401455767030500300270ustar00rootroot00000000000000require 'support/doubled_classes' module RSpec module Mocks RSpec.describe "Method visibility for verified doubles" do include_context "with isolated configuration" before do RSpec::Mocks.configuration.verify_doubled_constant_names = true end context "for an instance double (when the class is loaded)" do shared_examples "preserves method visibility" do |visibility| method_name = :"defined_#{visibility}_method" it "can allow a #{visibility} instance method" do o = instance_double('LoadedClass') allow(o).to receive(method_name).and_return(3) expect(o.send method_name).to eq(3) end it "can expect a #{visibility} instance method" do o = instance_double('LoadedClass') expect(o).to receive(method_name) o.send method_name end it "preserves #{visibility} visibility when allowing a #{visibility} method" do preserves_visibility(method_name, visibility) do instance_double('LoadedClass').tap do |o| allow(o).to receive(method_name) end end end it "preserves #{visibility} visibility when expecting a #{visibility} method" do preserves_visibility(method_name, visibility) do instance_double('LoadedClass').tap do |o| expect(o).to receive(method_name).at_least(:once) o.send(method_name) # to satisfy the expectation end end end it "preserves #{visibility} visibility on a null object" do preserves_visibility(method_name, visibility) do instance_double('LoadedClass').as_null_object end end end include_examples "preserves method visibility", :private include_examples "preserves method visibility", :protected end context "for a class double (when the class is loaded)" do shared_examples "preserves method visibility" do |visibility| method_name = :"defined_#{visibility}_class_method" it "can allow a #{visibility} instance method" do o = class_double('LoadedClass') allow(o).to receive(method_name).and_return(3) expect(o.send method_name).to eq(3) end it "can expect a #{visibility} instance method" do o = class_double('LoadedClass') expect(o).to receive(method_name) o.send method_name end it "preserves #{visibility} visibility when allowing a #{visibility} method" do preserves_visibility(method_name, visibility) do class_double('LoadedClass').tap do |o| allow(o).to receive(method_name) end end end it "preserves #{visibility} visibility when expecting a #{visibility} method" do preserves_visibility(method_name, visibility) do class_double('LoadedClass').tap do |o| expect(o).to receive(method_name).at_least(:once) o.send(method_name) # to satisfy the expectation end end end it "preserves #{visibility} visibility on a null object" do preserves_visibility(method_name, visibility) do class_double('LoadedClass').as_null_object end end end include_examples "preserves method visibility", :private include_examples "preserves method visibility", :protected end def preserves_visibility(method_name, visibility) double = yield expect { # send bypasses visibility, so we use eval instead. eval("double.#{method_name}") }.to raise_error(NoMethodError, a_message_indicating_visibility_violation(method_name, visibility)) expect { double.send(method_name) }.not_to raise_error expect { double.__send__(method_name) }.not_to raise_error unless double.null_object? # Null object doubles use `method_missing` and so the singleton class # doesn't know what methods are defined. singleton_class = class << double; self; end expect(singleton_class.send("#{visibility}_method_defined?", method_name)).to be true end end RSpec::Matchers.define :a_message_indicating_visibility_violation do |method_name, visibility| match do |msg| # This should NOT Be just `msg.match(visibility)` because the method being called # has the visibility name in it. We want to ensure it's a message that ruby is # stating is of the given visibility. msg.match("#{visibility} ") && msg.match(method_name.to_s) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/naming_spec.rb000066400000000000000000000050541455767030500255550ustar00rootroot00000000000000require 'support/doubled_classes' module RSpec module Mocks RSpec::Matchers.define :fail_expectations_as do |expected| description { "include a meaningful name in the exception" } def error_message_for(_) expect(actual).to have_received(:defined_instance_and_class_method) rescue MockExpectationError, Expectations::ExpectationNotMetError => e e.message else raise("should have failed but did not") end failure_message do |actual| "expected #{actual.inspect} to fail expectations as:\n" \ " #{expected.inspect}, but failed with:\n" \ " #{@error_message.inspect}" end match do |actual| @error_message = error_message_for(actual) @error_message.include?(expected) end end RSpec.describe 'Verified double naming' do shared_examples "a named verifying double" do |type_desc| context "when a name is given as a string" do subject { create_double("LoadedClass", "foo") } it { is_expected.to fail_expectations_as(%Q{#{type_desc}(LoadedClass) "foo"}) } end context "when a name is given as a symbol" do subject { create_double("LoadedClass", :foo) } it { is_expected.to fail_expectations_as(%Q{#{type_desc}(LoadedClass) :foo}) } end context "when no name is given" do subject { create_double("LoadedClass") } it { is_expected.to fail_expectations_as(%Q{#{type_desc}(LoadedClass) (anonymous)}) } end end describe "instance_double" do it_behaves_like "a named verifying double", "InstanceDouble" do alias :create_double :instance_double end end describe "instance_spy" do it_behaves_like "a named verifying double", "InstanceDouble" do alias :create_double :instance_spy end end describe "class_double" do it_behaves_like "a named verifying double", "ClassDouble" do alias :create_double :class_double end end describe "class_spy" do it_behaves_like "a named verifying double", "ClassDouble" do alias :create_double :class_spy end end describe "object_double" do it_behaves_like "a named verifying double", "ObjectDouble" do alias :create_double :object_double end end describe "object_spy" do it_behaves_like "a named verifying double", "ObjectDouble" do alias :create_double :object_spy end end end end end rspec-mocks-3.13.0/spec/rspec/mocks/verifying_doubles/object_double_spec.rb000066400000000000000000000127171455767030500271100ustar00rootroot00000000000000require 'support/doubled_classes' module RSpec module Mocks RSpec.describe 'An object double' do let(:loaded_instance) { LoadedClass.new(1, 2) } it 'can replace an unloaded constant' do o = object_double("LoadedClass::NOINSTANCE").as_stubbed_const expect(LoadedClass::NOINSTANCE).to eq(o) expect(o).to receive(:undefined_instance_method) o.undefined_instance_method end it 'can replace a constant by name and verify instance methods' do o = object_double("LoadedClass::INSTANCE").as_stubbed_const expect(LoadedClass::INSTANCE).to eq(o) prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_class_method) } prevents { o.defined_instance_method } expect(o).to receive(:defined_instance_method) o.defined_instance_method expect(o).to receive(:defined_private_method) o.send :defined_private_method end it 'can create a double that matches the interface of any arbitrary object' do o = object_double(loaded_instance) prevents { expect(o).to receive(:undefined_instance_method) } prevents { expect(o).to receive(:defined_class_method) } prevents { o.defined_instance_method } expect(o).to receive(:defined_instance_method) o.defined_instance_method expect(o).to receive(:defined_private_method) o.send :defined_private_method end it 'does not allow transferring constants to an object' do expect { object_double("LoadedClass::INSTANCE"). as_stubbed_const(:transfer_nested_constants => true) }.to raise_error(/Cannot transfer nested constants/) end it 'does not allow as_stubbed_constant for real objects' do expect { object_double(loaded_instance).as_stubbed_const }.to raise_error(/Can not perform constant replacement with an anonymous object/) end it 'is not a module' do expect(object_double("LoadedClass::INSTANCE")).to_not be_a(Module) end it 'validates `with` args against the method signature when stubbing a method' do dbl = object_double(loaded_instance) prevents(/Wrong number of arguments. Expected 2, got 3./) { allow(dbl).to receive(:instance_method_with_two_args).with(3, :foo, :args) } end context "when a loaded object constant has previously been stubbed with an object" do before { stub_const("LoadedClass::INSTANCE", Object.new) } it "uses the original object to verify against for `object_double('ConstName')`" do o = object_double("LoadedClass::INSTANCE") allow(o).to receive(:defined_instance_method) prevents { allow(o).to receive(:undefined_meth) } end it "uses the stubbed const value to verify against for `object_double(ConstName)`, " \ "which probably isn't what the user wants, but there's nothing else we can do since " \ "we can't get the constant name from the given object and thus cannot interrogate " \ "our stubbed const registry to see it has been stubbed" do o = object_double(LoadedClass::INSTANCE) prevents { allow(o).to receive(:defined_instance_method) } end end context "when a loaded object constant has previously been stubbed with a class" do before { stub_const("LoadedClass::INSTANCE", Class.new) } it "uses the original object to verify against for `object_double('ConstName')`" do o = object_double("LoadedClass::INSTANCE") allow(o).to receive(:defined_instance_method) prevents { allow(o).to receive(:undefined_meth) } end it "uses the original object to verify against for `object_double(ConstName)`" do o = object_double(LoadedClass::INSTANCE) allow(o).to receive(:defined_instance_method) prevents { allow(o).to receive(:undefined_meth) } end end context "when an unloaded object constant has previously been stubbed with an object" do before { stub_const("LoadedClass::NOINSTANCE", LoadedClass::INSTANCE) } it "treats it as being unloaded for `object_double('ConstName')`" do o = object_double("LoadedClass::NOINSTANCE") allow(o).to receive(:undefined_method) end it "uses the stubbed const value to verify against for `object_double(ConstName)`, " \ "which probably isn't what the user wants, but there's nothing else we can do since " \ "we can't get the constant name from the given object and thus cannot interrogate " \ "our stubbed const registry to see it has been stubbed" do o = object_double(LoadedClass::NOINSTANCE) allow(o).to receive(:defined_instance_method) prevents { allow(o).to receive(:undefined_method) } end end context "when an unloaded object constant has previously been stubbed with a class" do before { stub_const("LoadedClass::NOINSTANCE", Class.new) } it "treats it as being unloaded for `object_double('ConstName')`" do o = object_double("LoadedClass::NOINSTANCE") allow(o).to receive(:undefined_method) end it "treats it as being unloaded for `object_double(ConstName)`" do o = object_double(LoadedClass::NOINSTANCE) allow(o).to receive(:undefined_method) end end end end end rspec-mocks-3.13.0/spec/rspec/mocks_spec.rb000066400000000000000000000156761455767030500206000ustar00rootroot00000000000000require 'rspec/support/spec/library_wide_checks' RSpec.describe RSpec::Mocks do lib_preamble = [ # We define minitest constants because rspec/mocks/minitest_integration # expects these constants to already be defined. "module Minitest; class Assertion; end; module Test; end; end", 'require "rspec/mocks"', # Must be required before other files due to how our autoloads are setup. # (Users won't hit this problem because they won't require all the files # individually in whatever order the file system returns) 'require "rspec/mocks/any_instance"' ] # On 1.9.2 we load securerandom to get around the lack of `BasicObject#__id__. # Loading securerandom loads many other stdlibs it depends on. Rather than # declaring it (and all the stdlibs it loads) as allowed, it's easier just # to prevent the loading of securerandom by faking out `BasicObject#__id__ lib_preamble.unshift "class BasicObject; def __id__; end; end" if RUBY_VERSION == '1.9.2' it_behaves_like 'library wide checks', 'rspec-mocks', :preamble_for_lib => lib_preamble, :allowed_loaded_feature_regexps => [ /rbconfig/ # loaded by rspec-support ] do if RSpec::Support::Ruby.jruby? && JRUBY_VERSION =~ /9\.1\.7\.0/ before(:example, :description => /spec files/) do pending "JRuby 9.1.7.0 currently generates a circular warning which" \ " is unrelated to our suite." end end if RUBY_VERSION == '1.9.2' before(:example, :description => /spec files/) do pending "Loading psych and syck on 1.9.2 (as our test suite does) triggers warnings" end end end describe ".verify" do it "delegates to the space" do foo = double expect(foo).to receive(:bar) expect do RSpec::Mocks.verify end.to fail RSpec::Mocks.teardown # so the mocks aren't re-verified after this example end end describe ".teardown" do it "resets method stubs" do string = "foo".dup allow(string).to receive(:bar) RSpec::Mocks.teardown expect { string.bar }.to raise_error(NoMethodError) end it "does not put rspec-mocks into an inconsistent state when called extra times" do RSpec::Mocks.teardown RSpec::Mocks.teardown RSpec::Mocks.teardown string = "foo".dup expect { allow(string).to receive(:bar) }.to raise_error(RSpec::Mocks::OutsideOfExampleError) RSpec::Mocks.setup allow(string).to receive(:bar).and_return(:baz) expect(string.bar).to eq(:baz) end end describe ".setup" do it 'starts a new space scope that is later removed by .teardown' do old_space = RSpec::Mocks.space RSpec::Mocks.setup new_space = RSpec::Mocks.space expect(new_space).not_to equal(old_space) RSpec::Mocks.teardown expect(RSpec::Mocks.space).to equal(old_space) end end describe ".configuration" do it 'returns a memoized configuration instance' do expect(RSpec::Mocks.configuration).to be_a(RSpec::Mocks::Configuration) expect(RSpec::Mocks.configuration).to be(RSpec::Mocks.configuration) end end describe ".with_temporary_scope" do context "in a before(:all) with a stubbed double" do before(:all) do RSpec::Mocks.with_temporary_scope do @calculator = double allow(@calculator).to receive(:add) { |a, b| a + b } @sum = @calculator.add(3, 4) end capture_error { @calculator.add(1, 10) } end it 'allows the stubbed double to be used' do expect(@sum).to eq(7) end it 'does not allow the double to be used in the examples' do expect { @calculator.add(1, 2) }.to raise_error(RSpec::Mocks::ExpiredTestDoubleError) end it 'does not allow the double to be used after the scope in before(:all)' do expect(@error).to be_a(RSpec::Mocks::OutsideOfExampleError) end end context "in a before(:all) with a stubbed const" do before(:all) do RSpec::Mocks.with_temporary_scope do stub_const("ValueX", 3) stub_const("ValueY", 4) @sum = ValueX + ValueY end capture_error { ValueX + ValueY } end it 'allows the stubbed constants to be used' do expect(@sum).to eq(7) end it 'does not allow the stubbed constants to be used in the examples' do expect(defined?(ValueX)).to be_falsey expect(defined?(ValueY)).to be_falsey end it 'does not allow the stubbed constants to be used after the scope in before(:all)' do expect(@error).to be_a(NameError) expect(@error.message).to include("ValueX") end end context "in a before(:all) with an unmet mock expectation" do before(:all) do capture_error do RSpec::Mocks.with_temporary_scope do calculator = double expect(calculator).to receive(:add) end end end it 'fails with a mock expectation error' do expect(@error).to be_a(RSpec::Mocks::MockExpectationError) end end context "in a before(:all) with an any_instance stub" do before(:all) do RSpec::Mocks.with_temporary_scope do allow_any_instance_of(String).to receive(:sum_with) { |val, x| val + x } @sum = "foo".dup.sum_with("bar") end capture_error { "you".dup.sum_with("me") } end it 'allows the stub to be used' do expect(@sum).to eq("foobar") end it 'does not allow the double to be used in the examples' do expect { "foo".sum_with("baz") }.to raise_error(NameError, /sum_with/) end it 'does not allow the double to be used after the scope in before(:all)' do expect(@error).to be_a(NameError) expect(@error.message).to include("sum_with") end end it 'tears down even if an error occurs' do foo = Object.new expect { RSpec::Mocks.with_temporary_scope do allow(foo).to receive(:bar) raise "boom" end }.to raise_error("boom") expect(foo).not_to respond_to(:bar) end it 'does not verify if an error occurs before the block completes' do expect { RSpec::Mocks.with_temporary_scope do foo = Object.new expect(foo).to receive(:bar) raise "boom" end }.to raise_error("boom") # rather than MockExpectationError end it 'returns the result of the passed block' do expect(RSpec::Mocks.with_temporary_scope { 5 }).to eq 5 end def capture_error yield rescue Exception => @error end end context "when there is a `let` declaration that overrides an argument matcher" do let(:boolean) { :from_let } before do expect(RSpec::Mocks::ArgumentMatchers.method_defined?(:boolean)).to be true end it 'allows the `let` definition to win' do expect(boolean).to eq(:from_let) end end end rspec-mocks-3.13.0/spec/spec_helper.rb000066400000000000000000000102501455767030500176060ustar00rootroot00000000000000require 'rspec/support/spec' require 'rspec/support/ruby_features' RSpec::Support::Spec.setup_simplecov do minimum_coverage 93 end require 'yaml' begin require 'psych' rescue LoadError end RSpec::Matchers.define :include_method do |expected| match do |actual| actual.map { |m| m.to_s }.include?(expected.to_s) end end require 'support/matchers' module VerifyAndResetHelpers def verify(object) proxy = RSpec::Mocks.space.proxy_for(object) proxy.verify ensure proxy.reset # so it doesn't fail the verify after the example completes end def reset(object) RSpec::Mocks.space.proxy_for(object).reset end def verify_all RSpec::Mocks.space.verify_all ensure reset_all end def reset_all RSpec::Mocks.space.reset_all end def with_unfulfilled_double d = double("double") yield d ensure reset d end def expect_fast_failure_from(double, *fail_with_args, &blk) expect { blk.call(double) }.to fail_with(*fail_with_args) reset double end end module VerificationHelpers def prevents(msg=//, &block) expect(&block).to fail_with msg end end module MatcherHelpers def self.fake_matcher_description "fake_matcher_description" end extend RSpec::Matchers::DSL matcher :fake_matcher do |expected| match { |actual| actual == expected } description do MatcherHelpers.fake_matcher_description end end end require 'rspec/support/spec' RSpec.configure do |config| config.expose_dsl_globally = false config.mock_with :rspec config.color = true config.order = :random config.expect_with :rspec do |expectations| expectations.syntax = :expect end config.mock_with :rspec do |mocks| $default_rspec_mocks_syntax = mocks.syntax mocks.syntax = :expect end old_verbose = nil config.before(:each, :silence_warnings) do old_verbose = $VERBOSE $VERBOSE = nil end config.after(:each, :silence_warnings) do $VERBOSE = old_verbose end config.include VerifyAndResetHelpers config.include MatcherHelpers config.include VerificationHelpers config.extend RSpec::Support::RubyFeatures config.include RSpec::Support::RubyFeatures config.define_derived_metadata :ordered_and_vague_counts_unsupported do |meta| meta[:pending] = "`.ordered` combined with a vague count (e.g. `at_least` or `at_most`) is not yet supported (see #713)" end # We have yet to try to address this issue, and it's just noise in our output, # so skip it locally. However, on CI we want it to still run them so that if # we do something that makes these specs pass, we are notified. config.filter_run_excluding :ordered_and_vague_counts_unsupported unless ENV['CI'] # We don't want rspec-core to look in our `lib` for failure snippets. # When it does that, it inevitably finds this line: # `RSpec::Support.notify_failure(*args)` # ...which isn't very helpful. Far better for it to find the expectation # call site in the spec. config.project_source_dirs -= %w[ lib ] RSpec::Matchers.define_negated_matcher :a_string_excluding, :include end RSpec.shared_context "with syntax" do |syntax| orig_syntax = nil before(:all) do orig_syntax = RSpec::Mocks.configuration.syntax RSpec::Mocks.configuration.syntax = syntax end after(:all) do RSpec::Mocks.configuration.syntax = orig_syntax end end RSpec.shared_context "with isolated configuration" do orig_configuration = nil before do orig_configuration = RSpec::Mocks.configuration RSpec::Mocks.instance_variable_set(:@configuration, RSpec::Mocks::Configuration.new) end after do RSpec::Mocks.instance_variable_set(:@configuration, orig_configuration) end end RSpec.shared_context "with monkey-patched marshal" do before do RSpec::Mocks.configuration.patch_marshal_to_support_partial_doubles = true end after do RSpec::Mocks.configuration.patch_marshal_to_support_partial_doubles = false end end RSpec.shared_context "with the default mocks syntax" do orig_syntax = nil before(:all) do orig_syntax = RSpec::Mocks.configuration.syntax RSpec::Mocks.configuration.reset_syntaxes_to_default end after(:all) do RSpec::Mocks.configuration.syntax = orig_syntax end end rspec-mocks-3.13.0/spec/support/000077500000000000000000000000001455767030500165065ustar00rootroot00000000000000rspec-mocks-3.13.0/spec/support/aruba.rb000066400000000000000000000011151455767030500201230ustar00rootroot00000000000000begin require 'aruba/rspec' Aruba.configure do |config| if RUBY_PLATFORM =~ /java/ || defined?(Rubinius) || (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'truffleruby') config.exit_timeout = 60 else config.exit_timeout = 5 end end rescue NameError => e # This silences a name error on unsupported version of JRuby raise e unless RSpec::Support::Ruby.jruby? && JRUBY_VERSION =~ /9\.1\.17\.0/ rescue LoadError => e # This silences a load error on unsupported version of JRuby raise e unless RSpec::Support::Ruby.jruby? && JRUBY_VERSION =~ /9\.1\.17\.0/ end rspec-mocks-3.13.0/spec/support/before_all_shared_example_group.rb000066400000000000000000000007071455767030500254060ustar00rootroot00000000000000RSpec.shared_examples "fails in a before(:all) block" do the_error = nil before(:all) do begin use_rspec_mocks rescue the_error = $! end end it "raises an error with a useful message" do expect(the_error).to be_a_kind_of(RSpec::Mocks::OutsideOfExampleError) expect(the_error.message).to match(/The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported./) end end rspec-mocks-3.13.0/spec/support/doubled_classes.rb000066400000000000000000000027031455767030500221700ustar00rootroot00000000000000class LoadedClass extend RSpec::Support::RubyFeatures M = :m N = :n INSTANCE = LoadedClass.new def initialize(_a, _b) end class << self def respond_to?(method_name, include_all=false) return true if method_name == :dynamic_class_method super end def defined_class_method end def send # fake out! end def defined_instance_and_class_method end protected def defined_protected_class_method end private def defined_private_class_method end end def defined_instance_method end def instance_method_with_two_args(_a, _b) end def instance_method_with_only_defaults(_a=1, _b=2) end def defined_instance_and_class_method end if required_kw_args_supported? # Need to eval this since it is invalid syntax on earlier rubies. eval <<-RUBY def kw_args_method(foo, optional_arg:'hello', required_arg:) end def mixed_args_method(foo, bar, optional_arg_1:1, optional_arg_2:2) end RUBY end def send(*) end def respond_to?(method_name, include_all=false) return true if method_name == :dynamic_instance_method super end class Nested; end protected def defined_protected_method end private def defined_private_method "wink wink ;)" end end class LoadedClassWithOverriddenName < LoadedClass def self.name "Overriding name is not a good idea but we can't count on users not doing this" end end rspec-mocks-3.13.0/spec/support/matchers.rb000066400000000000000000000006131455767030500206410ustar00rootroot00000000000000module RSpec module Matchers def fail(&block) raise_error(RSpec::Mocks::MockExpectationError, &block) end def fail_with(*args, &block) raise_error(RSpec::Mocks::MockExpectationError, *args, &block) end def fail_including(*snippets) raise_error( RSpec::Mocks::MockExpectationError, a_string_including(*snippets) ) end end end rspec-mocks-3.13.0/tmp/000077500000000000000000000000001455767030500146405ustar00rootroot00000000000000rspec-mocks-3.13.0/tmp/.gitkeep000066400000000000000000000000001455767030500162570ustar00rootroot00000000000000