├── .ruby-version ├── spec ├── shared_examples │ └── .keep ├── spec_helper.rb ├── support │ ├── database.rb │ ├── database_cleaner.rb │ ├── matchers │ │ ├── pass_as_example.rb │ │ └── pass_as_describe_block.rb │ └── models.rb └── rspec_candy │ ├── helpers │ ├── stub_existing_spec.rb │ ├── stub_any_instance_spec.rb │ ├── should_receive_and_return_spec.rb │ ├── new_with_stubs_spec.rb │ ├── disposable_copy_spec.rb │ ├── should_receive_and_execute_spec.rb │ ├── rails │ │ ├── prevent_storage_spec.rb │ │ └── store_with_values_spec.rb │ ├── should_receive_chain_spec.rb │ └── it_should_act_like_spec.rb │ └── matchers │ ├── include_hash_spec.rb │ ├── be_same_number_as_spec.rb │ └── be_same_second_as_spec.rb ├── lib ├── rspec_candy.rb └── rspec_candy │ ├── version.rb │ ├── all.rb │ ├── base.rb │ ├── matchers.rb │ ├── matchers │ ├── be_same_second_as.rb │ ├── include_hash.rb │ └── be_same_number_as.rb │ ├── helpers │ ├── new_with_stubs.rb │ ├── should_receive_and_return.rb │ ├── disposable_copy.rb │ ├── rails │ │ ├── prevent_storage.rb │ │ └── store_with_values.rb │ ├── stub_existing.rb │ ├── stub_any_instance.rb │ ├── should_receive_chain.rb │ ├── it_should_act_like.rb │ └── should_receive_and_execute.rb │ ├── helpers.rb │ └── switcher.rb ├── .gitignore ├── Gemfile.rspec2-rails3 ├── Gemfile.rspec3-rails4 ├── .travis.yml ├── Gemfile.rspec1-rails2 ├── Gemfile.rspec1-rails2.lock ├── rspec_candy.gemspec ├── Rakefile ├── LICENSE ├── Gemfile.rspec2-rails3.lock ├── Gemfile.rspec3-rails4.lock └── README.md /.ruby-version: -------------------------------------------------------------------------------- 1 | 1.9.3 2 | -------------------------------------------------------------------------------- /spec/shared_examples/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/rspec_candy.rb: -------------------------------------------------------------------------------- 1 | # empty so bundler doesn't require a thing 2 | 3 | -------------------------------------------------------------------------------- /lib/rspec_candy/version.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | VERSION = '0.5.1' 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | doc 2 | pkg 3 | *.gem 4 | .idea 5 | spec/**/app_root/log/* 6 | Gemfile.lock 7 | -------------------------------------------------------------------------------- /lib/rspec_candy/all.rb: -------------------------------------------------------------------------------- 1 | require 'rspec_candy/helpers' 2 | require 'rspec_candy/matchers' 3 | -------------------------------------------------------------------------------- /lib/rspec_candy/base.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | require 'rspec_candy/version' 3 | require 'rspec_candy/switcher' 4 | -------------------------------------------------------------------------------- /lib/rspec_candy/matchers.rb: -------------------------------------------------------------------------------- 1 | require 'rspec_candy/base' 2 | require 'rspec_candy/matchers/include_hash' 3 | require 'rspec_candy/matchers/be_same_number_as' 4 | require 'rspec_candy/matchers/be_same_second_as' 5 | -------------------------------------------------------------------------------- /lib/rspec_candy/matchers/be_same_second_as.rb: -------------------------------------------------------------------------------- 1 | RSpecCandy::Switcher.define_matcher :be_same_second_as do |expected| 2 | 3 | match do |actual| 4 | actual.to_i == expected.to_i 5 | end 6 | 7 | 8 | end 9 | 10 | -------------------------------------------------------------------------------- /Gemfile.rspec2-rails3: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'activerecord', '~>3.2.0' 4 | gem 'rspec', '~>2.14.1' 5 | gem 'sqlite3' 6 | gem 'database_cleaner' 7 | gem 'rake' 8 | 9 | gem 'rspec_candy', :path => '.' 10 | -------------------------------------------------------------------------------- /Gemfile.rspec3-rails4: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'activerecord', '~>4.2.0' 4 | gem 'rspec', '~>3.2.0' 5 | gem 'sqlite3' 6 | gem 'database_cleaner' 7 | gem 'rake' 8 | 9 | gem 'rspec_candy', :path => '.' 10 | -------------------------------------------------------------------------------- /lib/rspec_candy/matchers/include_hash.rb: -------------------------------------------------------------------------------- 1 | RSpecCandy::Switcher.define_matcher :include_hash do |expected| 2 | 3 | match do |actual| 4 | !actual.nil? && expected.values == actual.values_at(*expected.keys) 5 | end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | $: << File.join(File.dirname(__FILE__), "/../../lib" ) 4 | 5 | require 'rspec_candy/all' 6 | 7 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} 8 | Dir["#{File.dirname(__FILE__)}/shared_examples/**/*.rb"].each {|f| require f} 9 | 10 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/new_with_stubs.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module NewWithStubs 4 | 5 | def new_with_stubs(stubs) 6 | obj = new 7 | obj.stub_existing stubs 8 | obj 9 | end 10 | 11 | Class.send(:include, self) 12 | 13 | end 14 | end 15 | end 16 | 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - "1.9.3" 4 | gemfile: 5 | - Gemfile.rspec1-rails2 6 | - Gemfile.rspec2-rails3 7 | - Gemfile.rspec3-rails4 8 | before_install: 9 | - gem update --system 2.4.3 # newer versions dont like bunlder 1.7.6 10 | - gem --version 11 | script: rake spec 12 | notifications: 13 | email: 14 | - fail@makandra.de 15 | -------------------------------------------------------------------------------- /spec/support/database.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:') 2 | 3 | ActiveRecord::Migration.class_eval do 4 | 5 | create_table :models do |t| 6 | t.string :string_field 7 | t.references :associated_model 8 | end 9 | 10 | create_table :sti_models do |t| 11 | t.string :type 12 | t.string :string_field 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /lib/rspec_candy/matchers/be_same_number_as.rb: -------------------------------------------------------------------------------- 1 | RSpecCandy::Switcher.define_matcher :be_same_number_as do |expected| 2 | 3 | match do |actual| 4 | actual.is_a?(Numeric) && expected.is_a?(Numeric) && normalize(expected) == normalize(actual) 5 | end 6 | 7 | def normalize(number) 8 | number.to_s.tap do |str| 9 | str << ".0" unless str.include?('.') 10 | end 11 | end 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/should_receive_and_return.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module ShouldReceiveAndReturn 4 | 5 | def should_receive_and_return(methods_and_values) 6 | methods_and_values.each do |method, value| 7 | should_receive(method).and_return(value) 8 | end 9 | self 10 | end 11 | 12 | Object.send(:include, self) 13 | 14 | end 15 | end 16 | end 17 | 18 | -------------------------------------------------------------------------------- /spec/support/database_cleaner.rb: -------------------------------------------------------------------------------- 1 | require 'database_cleaner' 2 | DatabaseCleaner.strategy = :deletion 3 | 4 | if RSpecCandy::Switcher.rspec_version == 1 5 | 6 | Spec::Runner.configure do |config| 7 | config.append_before do 8 | DatabaseCleaner.clean 9 | end 10 | end 11 | 12 | else 13 | 14 | RSpec.configure do |config| 15 | config.before do 16 | DatabaseCleaner.clean 17 | end 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/disposable_copy.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module DisposableCopy 4 | 5 | def disposable_copy(&body) 6 | this = self 7 | copy = Class.new(self) 8 | copy.singleton_class.send(:define_method, :name) { this.name } 9 | copy.class_eval(&body) if body 10 | copy 11 | end 12 | 13 | Class.send(:include, self) 14 | 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /Gemfile.rspec1-rails2: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | Deprecate.skip = true if defined?(Deprecate.skip) 4 | Gem::Deprecate.skip = true if defined?(Gem::Deprecate.skip) 5 | 6 | source 'https://rubygems.org' 7 | 8 | gem 'activerecord', '~>2.3.18' 9 | gem 'rspec', '~>1.3.2' 10 | gem 'sqlite3' 11 | gem 'database_cleaner' 12 | gem 'rake' 13 | 14 | gem 'rspec_candy', :path => '.' 15 | 16 | # gem 'test-unit', '~>1.2', :platforms => :ruby_19 17 | # gem 'hoe', '=2.8.0', :platforms => :ruby_19 18 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/rails/prevent_storage.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module Rails 4 | module PreventStorage 5 | 6 | def prevent_storage 7 | errors.stub :empty? => false 8 | end 9 | 10 | def keep_invalid! 11 | warn 'keep_invalid! is deprecated. Use prevent_storage instead.' 12 | prevent_storage 13 | end 14 | 15 | ActiveRecord::Base.send(:include, self) 16 | 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/stub_existing.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module StubExisting 4 | 5 | def stub_existing(attrs) 6 | attrs.each do |method, value| 7 | if respond_to?(method, true) 8 | stub(method => value) 9 | else 10 | raise "Attempted to stub non-existing method ##{method} on #{inspect}" 11 | end 12 | end 13 | end 14 | 15 | Object.send(:include, self) 16 | 17 | end 18 | end 19 | end 20 | 21 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/stub_any_instance.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module StubAnyInstance 4 | 5 | def stub_any_instance(stubs) 6 | case Switcher.rspec_version 7 | when 1 8 | unstubbed_new = method(:new) 9 | stub(:new).and_return do |*args| 10 | unstubbed_new.call(*args).tap do |obj| 11 | obj.stub stubs 12 | end 13 | end 14 | stubs 15 | else 16 | any_instance.stub(stubs) 17 | end 18 | end 19 | 20 | Class.send(:include, self) 21 | 22 | end 23 | end 24 | end 25 | 26 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers.rb: -------------------------------------------------------------------------------- 1 | require 'rspec_candy/base' 2 | require 'rspec_candy/helpers/disposable_copy' 3 | require 'rspec_candy/helpers/it_should_act_like' 4 | require 'rspec_candy/helpers/new_with_stubs' 5 | require 'rspec_candy/helpers/should_receive_and_execute' 6 | require 'rspec_candy/helpers/should_receive_and_return' 7 | require 'rspec_candy/helpers/should_receive_chain' 8 | require 'rspec_candy/helpers/stub_any_instance' 9 | require 'rspec_candy/helpers/stub_existing' 10 | 11 | if RSpecCandy::Switcher.active_record_loaded? 12 | require 'rspec_candy/helpers/rails/store_with_values' 13 | require 'rspec_candy/helpers/rails/prevent_storage' 14 | end 15 | -------------------------------------------------------------------------------- /Gemfile.rspec1-rails2.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | rspec_candy (0.5.1) 5 | rspec 6 | sneaky-save 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activerecord (2.3.18) 12 | activesupport (= 2.3.18) 13 | activesupport (2.3.18) 14 | database_cleaner (1.2.0) 15 | rake (10.4.2) 16 | rspec (1.3.2) 17 | sneaky-save (0.0.2) 18 | activerecord (>= 2.3.2) 19 | sqlite3 (1.3.10) 20 | 21 | PLATFORMS 22 | ruby 23 | 24 | DEPENDENCIES 25 | activerecord (~> 2.3.18) 26 | database_cleaner 27 | rake 28 | rspec (~> 1.3.2) 29 | rspec_candy! 30 | sqlite3 31 | 32 | BUNDLED WITH 33 | 1.12.5 34 | -------------------------------------------------------------------------------- /spec/support/matchers/pass_as_example.rb: -------------------------------------------------------------------------------- 1 | class RSpecRemote 2 | def self.run_example(example) 3 | run_describe_block(<<-describe_block) 4 | describe 'context' do 5 | it 'should pass' do 6 | #{example} 7 | end 8 | end 9 | describe_block 10 | end 11 | end 12 | 13 | 14 | RSpecCandy::Switcher.define_matcher :pass_as_example do 15 | 16 | match do |example| 17 | rspec_out = RSpecRemote.run_example(example) 18 | # puts rspec_out 19 | rspec_out.include?('0 failures') 20 | end 21 | 22 | end 23 | 24 | RSpecCandy::Switcher.define_matcher :fail_as_example do 25 | 26 | match do |example| 27 | rspec_out = RSpecRemote.run_example(example) 28 | # puts rspec_out 29 | rspec_out.include?('1 failure') 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/stub_existing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::StubExisting do 4 | 5 | describe Object do 6 | 7 | describe '#stub_existing' do 8 | 9 | it 'should stub an existing method' do 10 | string = 'foo' 11 | string.stub_existing(:upcase => 'BAR', :downcase => 'bar') 12 | string.upcase.should == 'BAR' 13 | string.downcase.should == 'bar' 14 | end 15 | 16 | it 'should raise an error when attempting to stub a non-existing method' do 17 | string = 'foo' 18 | expect do 19 | string.stub_existing(:unknown => 'value') 20 | end.to raise_error('Attempted to stub non-existing method #unknown on "foo"') 21 | end 22 | 23 | end 24 | 25 | end 26 | 27 | end 28 | -------------------------------------------------------------------------------- /rspec_candy.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | require "rspec_candy/version" 3 | 4 | Gem::Specification.new do |s| 5 | s.name = 'rspec_candy' 6 | s.version = RSpecCandy::VERSION 7 | s.authors = ["Henning Koch", "Tobias Kraze"] 8 | s.email = 'henning.koch@makandra.de' 9 | s.homepage = 'https://github.com/makandra/rspec_candy' 10 | s.summary = 'RSpec helpers and matchers we use in our daily work at makandra. ' 11 | s.description = s.summary 12 | s.license = 'MIT' 13 | 14 | s.files = `git ls-files`.split("\n") 15 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 16 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 17 | s.require_paths = ["lib"] 18 | 19 | s.add_dependency('rspec') 20 | s.add_dependency('sneaky-save') 21 | end 22 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/stub_any_instance_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::StubAnyInstance do 4 | 5 | describe Class do 6 | 7 | describe '#stub_any_instance' do 8 | 9 | it 'should apply the given stubs to all future instances of that class' do 10 | klass = Class.new {} 11 | klass.stub_any_instance(:stubbed_method => 'result') 12 | 2.times do 13 | klass.new.stubbed_method.should == 'result' 14 | end 15 | end 16 | 17 | it 'should not prevent initializer code to be run' do 18 | klass = Class.new do 19 | attr_reader :initializer_run 20 | def initialize 21 | @initializer_run = true 22 | end 23 | end 24 | klass.stub_any_instance(:stubbed_method => 'result') 25 | klass.new.initializer_run.should == true 26 | end 27 | 28 | end 29 | 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/should_receive_and_return_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::StubExisting do 4 | 5 | describe Object do 6 | 7 | describe '#should_receive_and_return' do 8 | 9 | it 'should stub out the given methods with the given return values' do 10 | object = 'object' 11 | object.should_receive_and_return(:foo => 'foo', :bar => 'bar') 12 | object.foo.should == 'foo' 13 | object.bar.should == 'bar' 14 | object.size.should == 6 # object still responds to unstubbed methods 15 | end 16 | 17 | it 'should set expectations that all given methods are called' do 18 | (<<-example).should fail_as_example 19 | object = 'object' 20 | object.should_receive_and_return(:foo => 'foo', :bar => 'bar') 21 | object.foo 22 | # but not object.bar 23 | example 24 | end 25 | 26 | end 27 | 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /spec/support/models.rb: -------------------------------------------------------------------------------- 1 | class Model < ActiveRecord::Base 2 | 3 | after_create :after_create_callback 4 | after_save :after_save_callback1, :after_save_callback2 5 | before_save :before_save_callback1, :before_save_callback2 6 | 7 | belongs_to :associated_model, :class_name => 'Model' 8 | 9 | if respond_to? :around_save 10 | around_save :around_save_callback1, :around_save_callback2 11 | end 12 | 13 | def after_create_callback; end 14 | def after_save_callback1; end 15 | def after_save_callback2; end 16 | def before_save_callback1; end 17 | def before_save_callback2; end 18 | def around_save_callback1; yield; end 19 | def around_save_callback2; yield; end 20 | 21 | end 22 | 23 | 24 | class StiParent < ActiveRecord::Base 25 | 26 | self.table_name = 'sti_models' 27 | 28 | before_save :crash 29 | validate :crash 30 | 31 | def crash 32 | raise 'callback was run' 33 | end 34 | 35 | end 36 | 37 | 38 | class StiChild < StiParent 39 | 40 | end 41 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'bundler/gem_tasks' 3 | 4 | desc 'Default: Run all specs.' 5 | task :default => 'all:spec' 6 | 7 | task :spec do 8 | rspec_binary = ENV['BUNDLE_GEMFILE'].include?('rspec1') ? 'spec' : 'rspec' 9 | examples = ENV['SPEC'] || 'spec' 10 | success &= system("bundle exec #{rspec_binary} #{examples}") 11 | end 12 | 13 | namespace :all do 14 | 15 | desc "Run specs on all versions" 16 | task :spec do 17 | success = true 18 | for_each_gemfile do 19 | Rake::Task['spec'].execute 20 | end 21 | fail "Tests failed" unless success 22 | end 23 | 24 | desc "Bundle all versions" 25 | task :bundle do 26 | for_each_gemfile do 27 | system('bundle install') 28 | end 29 | end 30 | 31 | end 32 | 33 | def for_each_gemfile 34 | version = ENV['VERSION'] || '*' 35 | Dir["./Gemfile.#{version}"].sort.each do |gemfile| 36 | next if gemfile =~ /.lock/ 37 | puts '', "\033[44m#{gemfile}\033[0m", '' 38 | ENV['BUNDLE_GEMFILE'] = gemfile 39 | yield 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 makandra 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /spec/rspec_candy/matchers/include_hash_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy do 4 | describe 'matchers' do 5 | describe 'include_hash' do 6 | 7 | it 'should match if all pairs in the given hash exist in the receiver' do 8 | { :foo => 'a', :bar => 'b' , :bam => 'c'}.should include_hash(:foo => 'a', :bam => 'c') 9 | end 10 | 11 | it 'should not match if the given hash has keys that do not exist in the receiver' do 12 | { :foo => 'a', :bar => 'b' , :bam => 'c'}.should_not include_hash(:foo => 'a', :baz => 'd') 13 | end 14 | 15 | it 'should not match if the given hash has values that are different in the receiver' do 16 | { :foo => 'a', :bar => 'b' , :bam => 'c'}.should_not include_hash(:foo => 'a', :bam => 'different-value') 17 | end 18 | 19 | it 'should not crash and not match if the receiver is nil' do 20 | nil.should_not include_hash(:foo => 'a') 21 | end 22 | 23 | it 'should match if the receiver is empty and the given hash is empty' do 24 | {}.should include_hash({}) 25 | end 26 | 27 | end 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /spec/rspec_candy/matchers/be_same_number_as_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy do 4 | describe 'matchers' do 5 | describe 'be_same_number_as' do 6 | 7 | it 'should correctly compare Fixnums and Floats' do 8 | 1.should be_same_number_as(1.0) 9 | 2.should_not be_same_number_as(1.0) 10 | -1.should be_same_number_as(-1.0) 11 | -2.should_not be_same_number_as(-1.0) 12 | end 13 | 14 | it 'should correctly compare Fixnums and BigDecimals' do 15 | 1.should be_same_number_as(BigDecimal('1.0')) 16 | 2.should_not be_same_number_as(BigDecimal('1.0')) 17 | -1.should be_same_number_as(BigDecimal('-1.0')) 18 | -2.should_not be_same_number_as(BigDecimal('-1.0')) 19 | end 20 | 21 | it 'should correctly compare Floats and BigDecimals' do 22 | 1.1.should be_same_number_as(BigDecimal('1.1')) 23 | 2.1.should_not be_same_number_as(BigDecimal('1.1')) 24 | -1.1.should be_same_number_as(BigDecimal('-1.1')) 25 | -2.1.should_not be_same_number_as(BigDecimal('-1.1')) 26 | end 27 | 28 | end 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/new_with_stubs_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::NewWithStubs do 4 | 5 | describe Class do 6 | 7 | describe '#new_with_stubs' 8 | 9 | it 'should instantiate a class with an empty constructor and stub out the given methods' do 10 | klass = Class.new do 11 | def initialize 12 | end 13 | def foo 14 | 'stubbed foo' 15 | end 16 | def bar 17 | 'unstubbed bar' 18 | end 19 | def unstubbed_method 20 | 'unstubbed result' 21 | end 22 | end 23 | instance = klass.new_with_stubs(:foo => 'stubbed foo', :bar => 'stubbed bar') 24 | instance.foo.should == 'stubbed foo' 25 | instance.bar.should == 'stubbed bar' 26 | instance.unstubbed_method.should == 'unstubbed result' 27 | end 28 | 29 | it 'should raise an error when trying to stub non-existing methods' do 30 | klass = Class.new do 31 | def initialize 32 | end 33 | end 34 | expect do 35 | klass.new_with_stubs(:foo => 'foo') 36 | end.to raise_error(/Attempted to stub non-existing method/) 37 | end 38 | 39 | end 40 | 41 | end -------------------------------------------------------------------------------- /lib/rspec_candy/switcher.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Switcher 3 | extend self 4 | 5 | def rspec_version 6 | begin 7 | require 'rspec/version' 8 | RSpec::Version::STRING.to_i 9 | rescue LoadError 10 | if defined?(Spec) 11 | 1 12 | else 13 | raise 'Cannot determine RSpec version' 14 | end 15 | end 16 | end 17 | 18 | def active_record_version 19 | ActiveRecord::VERSION::MAJOR 20 | end 21 | 22 | def active_record_loaded? 23 | defined?(ActiveRecord) 24 | end 25 | 26 | def new_mock(*args) 27 | case rspec_version 28 | when 1 29 | Spec::Mocks::Mock.new(*args) 30 | when 2 31 | RSpec::Mocks::Mock.new(*args) 32 | else 33 | RSpec::Mocks::Double.new(*args) 34 | end 35 | end 36 | 37 | def rspec_root 38 | if rspec_version == 1 39 | Spec 40 | else 41 | RSpec 42 | end 43 | end 44 | 45 | def rspec_matcher_registry 46 | rspec_root.const_get(:Matchers) 47 | end 48 | 49 | def define_matcher(*args, &block) 50 | rspec_matcher_registry.define(*args, &block) 51 | end 52 | 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/support/matchers/pass_as_describe_block.rb: -------------------------------------------------------------------------------- 1 | require 'tempfile' 2 | 3 | class RSpecRemote 4 | def self.run_describe_block(describe_block) 5 | temp_path = nil 6 | Tempfile.open(['example', '_spec.rb']) do |io| 7 | io.write(<<-spec) 8 | require 'spec_helper'; 9 | #{describe_block} 10 | spec 11 | temp_path = io.path 12 | end 13 | `rake SPEC=#{temp_path} 2>&1` 14 | end 15 | end 16 | 17 | RSpecCandy::Switcher.define_matcher :pass_as_describe_block do 18 | 19 | match do |describe_block| 20 | rspec_out = RSpecRemote.run_describe_block(describe_block) 21 | passes = rspec_out.include?('0 failures') 22 | # unless passes 23 | # puts "Expected RSpec output to not have failures:" 24 | # puts rspec_out 25 | # end 26 | passes 27 | end 28 | 29 | end 30 | 31 | RSpecCandy::Switcher.define_matcher :fail_as_describe_block do 32 | 33 | match do |describe_block| 34 | rspec_out = RSpecRemote.run_describe_block(describe_block) 35 | passes = rspec_out.include?('1 failure') 36 | # unless passes 37 | # puts "Expected RSpec output to not have failures:" 38 | # puts rspec_out 39 | # end 40 | passes 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/should_receive_chain.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module ShouldReceiveChain 4 | 5 | def should_receive_chain(*parts) 6 | setup_expectation_chain(parts) 7 | end 8 | 9 | def should_not_receive_chain(*parts) 10 | setup_expectation_chain(parts, :negate => true) 11 | end 12 | 13 | private 14 | 15 | def setup_expectation_chain(parts, options = {}) 16 | obj = self 17 | for part in parts 18 | if part == parts.last 19 | expectation = options[:negate] ? :should_not_receive : :should_receive 20 | obj = add_expectation_chain_link(obj, expectation, part) 21 | else 22 | next_obj = Switcher.new_mock('chain link') 23 | add_expectation_chain_link(obj, :stub, part).and_return(next_obj) 24 | obj = next_obj 25 | end 26 | end 27 | obj 28 | end 29 | 30 | def add_expectation_chain_link(obj, expectation, part) 31 | if part.is_a?(Array) 32 | obj.send(expectation, part.first).with(*part[1..-1]) 33 | else 34 | obj.send(expectation, part) 35 | end 36 | end 37 | 38 | Object.send(:include, self) 39 | 40 | end 41 | end 42 | end 43 | 44 | -------------------------------------------------------------------------------- /Gemfile.rspec2-rails3.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | rspec_candy (0.5.1) 5 | rspec 6 | sneaky-save 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activemodel (3.2.21) 12 | activesupport (= 3.2.21) 13 | builder (~> 3.0.0) 14 | activerecord (3.2.21) 15 | activemodel (= 3.2.21) 16 | activesupport (= 3.2.21) 17 | arel (~> 3.0.2) 18 | tzinfo (~> 0.3.29) 19 | activesupport (3.2.21) 20 | i18n (~> 0.6, >= 0.6.4) 21 | multi_json (~> 1.0) 22 | arel (3.0.3) 23 | builder (3.0.4) 24 | database_cleaner (1.2.0) 25 | diff-lcs (1.2.5) 26 | i18n (0.7.0) 27 | multi_json (1.11.0) 28 | rake (10.4.2) 29 | rspec (2.14.1) 30 | rspec-core (~> 2.14.0) 31 | rspec-expectations (~> 2.14.0) 32 | rspec-mocks (~> 2.14.0) 33 | rspec-core (2.14.8) 34 | rspec-expectations (2.14.5) 35 | diff-lcs (>= 1.1.3, < 2.0) 36 | rspec-mocks (2.14.6) 37 | sneaky-save (0.1.3) 38 | activerecord (>= 3.2.0) 39 | sqlite3 (1.3.10) 40 | tzinfo (0.3.43) 41 | 42 | PLATFORMS 43 | ruby 44 | 45 | DEPENDENCIES 46 | activerecord (~> 3.2.0) 47 | database_cleaner 48 | rake 49 | rspec (~> 2.14.1) 50 | rspec_candy! 51 | sqlite3 52 | 53 | BUNDLED WITH 54 | 1.12.5 55 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/rails/store_with_values.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module Rails 4 | module StoreWithValues 5 | 6 | def store_with_values(values = {}) 7 | record = new 8 | case Switcher.active_record_version 9 | when 2 10 | record.send(:attributes=, values, false) 11 | record.send(:create_without_callbacks) 12 | when 3 13 | require 'sneaky-save' 14 | record.assign_attributes(values, :without_protection => true) 15 | record.sneaky_save 16 | else 17 | require 'sneaky-save' 18 | record.assign_attributes(values) 19 | record.sneaky_save 20 | end 21 | record 22 | end 23 | 24 | def new_and_store(*args) 25 | warn 'new_and_store is deprecated. Use store_with_values instead.' 26 | store_with_values(*args) 27 | end 28 | 29 | def create_without_callbacks(*args) 30 | warn 'create_without_callbacks is deprecated because the name suggested that it honors mass-assignment protection. Use store_with_values instead.' 31 | store_with_values(*args) 32 | end 33 | 34 | ActiveRecord::Base.send(:extend, self) 35 | 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/rspec_candy/matchers/be_same_second_as_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy do 4 | describe 'matchers' do 5 | describe 'be_same_second_as' do 6 | 7 | it 'should consider equal two Times with the same second' do 8 | Time.parse('2012-05-01 14:15:16').should be_same_second_as(Time.parse('2012-05-01 14:15:16')) 9 | Time.parse('2012-05-01 14:15:17').should_not be_same_second_as(Time.parse('2012-05-01 14:15:16')) 10 | end 11 | 12 | it 'should ignore sub-second differences' do 13 | Time.parse('2012-05-01 00:00:00.1').should be_same_second_as(Time.parse('2012-05-01 00:00:00.2')) 14 | end 15 | 16 | it 'should correctly compare Time and DateTime objects' do 17 | Time.parse('2012-05-01 14:15:16 +0000').should be_same_second_as(DateTime.parse('2012-05-01 14:15:16 +0000')) 18 | Time.parse('2012-05-01 14:15:17 +0000').should_not be_same_second_as(DateTime.parse('2012-05-01 14:15:16 +0000')) 19 | Time.parse('2012-05-01 00:00:00.1 +0000').should be_same_second_as(DateTime.parse('2012-05-01 00:00:00.2 +0000')) 20 | end 21 | 22 | it 'should consider different two times in different zones' do 23 | Time.parse('2012-05-01 14:15:16 +0000').should_not be_same_second_as(Time.parse('2012-05-01 14:15:16 +0100')) 24 | end 25 | 26 | end 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/it_should_act_like.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module ItShouldActLike 4 | 5 | def self.included(by) 6 | by.class_eval do 7 | 8 | # Improves it_should_behave_like in some ways: 9 | # - It scopes the reused examples so #let und #subject does not bleed into the reusing example groups 10 | # - It allows to parametrize the reused example group by appending a hash argument. 11 | # Every key/value pair in the hash will become a #let variable for the reused example group 12 | # - You can call it with a block. It will be available to the reused example group as let(:block) 13 | def it_should_act_like(shared_example_group, environment = {}, &block) 14 | description = "as #{shared_example_group}" 15 | description << " (#{environment.inspect})" if environment.present? 16 | describe description do 17 | environment.each do |name, value| 18 | let(name) { value } 19 | end 20 | let(:block) { block } if block 21 | it_should_behave_like(shared_example_group) 22 | end 23 | end 24 | end 25 | end 26 | 27 | case Switcher.rspec_version 28 | when 1 29 | Spec::Example::ExampleGroupMethods.send(:include, self) 30 | else 31 | RSpec::Core::ExampleGroup.singleton_class.send(:include, self) 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /Gemfile.rspec3-rails4.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | rspec_candy (0.5.1) 5 | rspec 6 | sneaky-save 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activemodel (4.2.1) 12 | activesupport (= 4.2.1) 13 | builder (~> 3.1) 14 | activerecord (4.2.1) 15 | activemodel (= 4.2.1) 16 | activesupport (= 4.2.1) 17 | arel (~> 6.0) 18 | activesupport (4.2.1) 19 | i18n (~> 0.7) 20 | json (~> 1.7, >= 1.7.7) 21 | minitest (~> 5.1) 22 | thread_safe (~> 0.3, >= 0.3.4) 23 | tzinfo (~> 1.1) 24 | arel (6.0.0) 25 | builder (3.2.2) 26 | database_cleaner (1.2.0) 27 | diff-lcs (1.2.5) 28 | i18n (0.7.0) 29 | json (1.8.2) 30 | minitest (5.5.1) 31 | rake (10.4.2) 32 | rspec (3.2.0) 33 | rspec-core (~> 3.2.0) 34 | rspec-expectations (~> 3.2.0) 35 | rspec-mocks (~> 3.2.0) 36 | rspec-core (3.2.3) 37 | rspec-support (~> 3.2.0) 38 | rspec-expectations (3.2.1) 39 | diff-lcs (>= 1.2.0, < 2.0) 40 | rspec-support (~> 3.2.0) 41 | rspec-mocks (3.2.1) 42 | diff-lcs (>= 1.2.0, < 2.0) 43 | rspec-support (~> 3.2.0) 44 | rspec-support (3.2.2) 45 | sneaky-save (0.1.3) 46 | activerecord (>= 3.2.0) 47 | sqlite3 (1.3.10) 48 | thread_safe (0.3.5) 49 | tzinfo (1.2.2) 50 | thread_safe (~> 0.1) 51 | 52 | PLATFORMS 53 | ruby 54 | 55 | DEPENDENCIES 56 | activerecord (~> 4.2.0) 57 | database_cleaner 58 | rake 59 | rspec (~> 3.2.0) 60 | rspec_candy! 61 | sqlite3 62 | 63 | BUNDLED WITH 64 | 1.12.5 65 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/disposable_copy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::DisposableCopy do 4 | 5 | describe Class do 6 | 7 | describe '.disposable_copy' do 8 | 9 | it 'should return a class' do 10 | Model.disposable_copy.should be_a(Class) 11 | end 12 | 13 | it 'should return a class with the same name as the original class' do 14 | Model.disposable_copy.name.should == 'Model' 15 | end 16 | 17 | it 'should return a class that instantiates objects that are also instances of the original class' do 18 | instance = Model.disposable_copy.new 19 | instance.should be_a(Model) 20 | end 21 | 22 | it 'should return a class that can be modified without changing the original class' do 23 | copy = Model.disposable_copy 24 | copy.class_eval do 25 | def foo 26 | end 27 | end 28 | copy.new.should respond_to(:foo) 29 | Model.new.should_not respond_to(:foo) 30 | end 31 | 32 | it 'should take a block with is evaluated in the context of the disposable class' do 33 | copy = Model.disposable_copy do 34 | def foo 35 | end 36 | end 37 | copy.new.should respond_to(:foo) 38 | Model.new.should_not respond_to(:foo) 39 | end 40 | 41 | it 'should evaluate the block after the copy has been renamed to the original class name' do 42 | spy = double('spy') 43 | spy.should_receive(:observe_name).with('Model') 44 | Model.disposable_copy do 45 | spy.observe_name(name) 46 | end 47 | end 48 | 49 | end 50 | 51 | end 52 | 53 | end 54 | -------------------------------------------------------------------------------- /lib/rspec_candy/helpers/should_receive_and_execute.rb: -------------------------------------------------------------------------------- 1 | module RSpecCandy 2 | module Helpers 3 | module ShouldReceiveAndExecute 4 | 5 | def should_receive_and_execute(method) 6 | 7 | method = method.to_s 8 | method_base = method.gsub(/([\?\!\=\[\]]+)$/, '') 9 | method_suffix = $1 10 | 11 | method_called = "_#{method_base}_called#{method_suffix}" 12 | method_with_spy = "#{method_base}_with_spy#{method_suffix}" 13 | method_without_spy = "#{method_base}_without_spy#{method_suffix}" 14 | 15 | prototype = respond_to?(:singleton_class) ? singleton_class : metaclass 16 | prototype.class_eval do 17 | 18 | method_defined_directly = method_defined?(method) || private_method_defined?(method) # check that a method is not "defined" by responding to method_missing 19 | 20 | unless method_defined?(method_called) 21 | define_method method_called do |*args| 22 | end 23 | end 24 | 25 | if method_defined_directly 26 | unless method_defined?(method_with_spy) 27 | define_method method_with_spy do |*args, &block| 28 | send(method_called, *args) 29 | send(method_without_spy, *args, &block) 30 | end 31 | alias_method_chain method, :spy 32 | end 33 | else 34 | define_method method do |*args, &block| 35 | send(method_called, *args) 36 | super(*args, &block) 37 | end 38 | end 39 | 40 | end 41 | 42 | should_receive(method_called) 43 | end 44 | 45 | Object.send(:include, self) 46 | 47 | end 48 | end 49 | end 50 | 51 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/should_receive_and_execute_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::ShouldReceiveAndExecute do 4 | 5 | describe Object do 6 | 7 | describe '#should_receive_and_execute' do 8 | 9 | it 'should not stub away the original method implementation' do 10 | object = "object" 11 | object.should_receive_and_execute(:size) 12 | object.size.should == 6 13 | end 14 | 15 | it 'should set an expectation that the given method are called' do 16 | (<<-example).should fail_as_example 17 | object = 'object' 18 | object.should_receive_and_execute(:size) 19 | example 20 | end 21 | 22 | it 'should return the expectation for further parameterization' do 23 | object = 'object' 24 | object.should_receive_and_execute(:size).class.name.should include('MessageExpectation') 25 | object.size # make the example pass 26 | end 27 | 28 | it "should not overwrite the implementation of a class method defined through an object's singleton class" do 29 | object = Object.new 30 | def object.foo 31 | 'foo' 32 | end 33 | object.should_receive_and_execute(:foo) 34 | object.foo.should == 'foo' 35 | end 36 | 37 | it 'should not fail for a class that responds to messages using #method_missing' do 38 | klass = Class.new do 39 | def respond_to?(*args) 40 | true 41 | end 42 | def method_missing(symbol, *args, &block) 43 | 'foo' 44 | end 45 | end 46 | object = klass.new 47 | object.should_receive_and_execute(:foo) 48 | object.foo.should == 'foo' 49 | end 50 | 51 | it 'should work when setting multiple expectations on the same method' do 52 | object = "16" 53 | object.should_receive_and_execute(:to_i).with(10) 54 | object.should_receive_and_execute(:to_i).with(8) 55 | object.to_i(10).should == 16 56 | object.to_i(8).should == 14 57 | end 58 | 59 | end 60 | 61 | end 62 | 63 | end 64 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/rails/prevent_storage_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::Rails::PreventStorage do 4 | 5 | describe ActiveRecord::Base do 6 | 7 | describe '#prevent_storage' do 8 | 9 | context 'when attempting to save the record afterwards' do 10 | 11 | it 'should run validations' do 12 | klass = Model.disposable_copy do 13 | validate :validation_method 14 | end 15 | record = klass.new 16 | record.prevent_storage 17 | record.should_receive :validation_method 18 | record.save 19 | end 20 | 21 | it 'should run before_validate callbacks' do 22 | klass = Model.disposable_copy do 23 | before_validation :callback_method 24 | end 25 | record = klass.new 26 | record.prevent_storage 27 | record.should_receive :callback_method 28 | record.save 29 | end 30 | 31 | it 'should run after_validate callbacks' do 32 | klass = Model.disposable_copy do 33 | after_validation :callback_method 34 | end 35 | record = klass.new 36 | record.prevent_storage 37 | record.should_receive :callback_method 38 | record.save 39 | end 40 | 41 | it 'should prevent the record from being committed to the database' do 42 | record = Model.new 43 | record.prevent_storage 44 | record.save.should == false 45 | Model.count.should be_zero 46 | end 47 | 48 | it 'should not run before_save callbacks' do 49 | klass = Model.disposable_copy do 50 | before_save :callback_method 51 | end 52 | record = klass.new 53 | record.prevent_storage 54 | record.should_not_receive :callback_method 55 | record.save 56 | end 57 | 58 | it 'should not run after_save callbacks' do 59 | klass = Model.disposable_copy do 60 | after_save :callback_method 61 | end 62 | record = klass.new 63 | record.prevent_storage 64 | record.should_not_receive :callback_method 65 | record.save 66 | end 67 | 68 | end 69 | 70 | end 71 | 72 | describe '#keep_invalid!' do 73 | 74 | it 'should print a deprecation warning asking to use #prevent_storage' do 75 | record = Model.new 76 | record.should_receive(:warn).with(/Use prevent_storage instead/i) 77 | record.should_receive(:prevent_storage) 78 | record.keep_invalid! 79 | end 80 | 81 | end 82 | 83 | end 84 | 85 | end 86 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/rails/store_with_values_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::Rails::StoreWithValues do 4 | 5 | describe ActiveRecord::Base do 6 | 7 | describe '.store_with_values' do 8 | 9 | it 'should run after_initialize callbacks' do 10 | model = Model.disposable_copy do 11 | after_initialize :set_defaults 12 | def after_initialize # because old Rails 13 | end 14 | def set_defaults 15 | self.string_field = 'Hello Universe' 16 | end 17 | end 18 | 19 | record = model.store_with_values 20 | record.string_field.should == 'Hello Universe' 21 | end 22 | 23 | it 'should allow setting associations' do 24 | associated_model = Model.create! 25 | 26 | record = Model.store_with_values(:associated_model => associated_model) 27 | record.reload 28 | record.associated_model.should == associated_model 29 | end 30 | 31 | it 'should allow setting primary keys' do 32 | record = Model.store_with_values(:id => 1337) 33 | record.id.should == 1337 34 | end 35 | 36 | it 'should create a record without running callbacks or validations' do 37 | model = Model.disposable_copy do 38 | before_save :crash 39 | validate :crash 40 | def crash 41 | raise 'callback was run' 42 | end 43 | end 44 | record = nil 45 | expect do 46 | record = model.store_with_values(:string_field => 'foo') 47 | end.to_not raise_error 48 | record.string_field.should == 'foo' 49 | record.should be_a(Model) 50 | record.id.should be_a(Fixnum) 51 | record.should_not be_new_record 52 | end 53 | 54 | it 'should work with single table inheritance' do 55 | child = StiChild.store_with_values(:string_field => 'foo') 56 | child.string_field.should == 'foo' 57 | child.should be_a(StiChild) 58 | child.id.should be_a(Fixnum) 59 | child.type.should == 'StiChild' 60 | child.should_not be_new_record 61 | StiChild.all.should == [child] 62 | end 63 | 64 | end 65 | 66 | describe '.create_without_callbacks' do 67 | 68 | it 'should print a deprecation warning urging to use .store_with_values instead' do 69 | Model.should_receive(:warn).with(/Use store_with_values instead/i) 70 | Model.should_receive(:store_with_values).with('args') 71 | Model.create_without_callbacks('args') 72 | end 73 | 74 | end 75 | 76 | describe '.new_and_store' do 77 | 78 | it 'should print a deprecation warning urging to use .store_with_values instead' do 79 | Model.should_receive(:warn).with(/Use store_with_values instead/i) 80 | Model.should_receive(:store_with_values).with('args') 81 | Model.new_and_store('args') 82 | end 83 | 84 | end 85 | 86 | end 87 | 88 | end 89 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/should_receive_chain_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::ShouldReceiveChain do 4 | 5 | describe Object do 6 | 7 | describe '#should_receive_chain' do 8 | 9 | it "should pass when the chain was traversed completely" do 10 | (<<-example).should pass_as_example 11 | object = "object" 12 | object.should_receive_chain(:first_message, :second_message) 13 | object.first_message.second_message 14 | example 15 | end 16 | 17 | it "should fail when the chain wasn't traversed at all" do 18 | (<<-example).should fail_as_example 19 | object = "object" 20 | object.should_receive_chain(:first_message, :second_message) 21 | example 22 | end 23 | 24 | it "should fail when the chain was only traversed partially" do 25 | (<<-example).should fail_as_example 26 | object = "object" 27 | object.should_receive_chain(:first_message, :second_message) 28 | object.first_message 29 | example 30 | end 31 | 32 | it "should allow to add argument expectations to inner chain links by using array notation" do 33 | (<<-example).should pass_as_example 34 | object = "object" 35 | object.should_receive_chain([:first_message, 'argument'], :second_message) 36 | object.first_message('argument').second_message 37 | example 38 | (<<-example).should fail_as_example 39 | object = "object" 40 | object.should_receive_chain([:first_message, 'argument'], :second_message) 41 | object.first_message('wrong argument').second_message 42 | example 43 | end 44 | 45 | it "should allow to add argument expectations to the last chain link by using regular should_receive options" do 46 | (<<-example).should pass_as_example 47 | object = "object" 48 | object.should_receive_chain(:first_message, :second_message).with('argument') 49 | object.first_message.second_message('argument') 50 | example 51 | (<<-example).should fail_as_example 52 | object = "object" 53 | object.should_receive_chain(:first_message, :second_message).with('argument') 54 | object.first_message.second_message('wrong argument') 55 | example 56 | end 57 | 58 | it 'should allow inner chain links to be called more than once' do 59 | (<<-example).should pass_as_example 60 | object = "object" 61 | object.should_receive_chain(:first_message, :second_message) 62 | object.first_message 63 | object.first_message.second_message 64 | example 65 | end 66 | 67 | end 68 | 69 | 70 | describe '#should_not_receive_chain' do 71 | 72 | it "should fail when the chain was traversed completely" do 73 | (<<-example).should fail_as_example 74 | object = "object" 75 | object.should_not_receive_chain(:first_message, :second_message) 76 | object.first_message.second_message 77 | example 78 | end 79 | 80 | it "should pass when the chain wasn't traversed at all" do 81 | (<<-example).should pass_as_example 82 | object = "object" 83 | object.should_not_receive_chain(:first_message, :second_message) 84 | example 85 | end 86 | 87 | it "should pass when the chain was only traversed partially" do 88 | (<<-example).should pass_as_example 89 | object = "object" 90 | object.should_not_receive_chain(:first_message, :second_message) 91 | object.first_message 92 | example 93 | end 94 | 95 | end 96 | 97 | end 98 | 99 | end 100 | -------------------------------------------------------------------------------- /spec/rspec_candy/helpers/it_should_act_like_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RSpecCandy::Helpers::StubAnyInstance do 4 | describe 'RSpec example group' do 5 | 6 | describe '#it_should_act_like' do 7 | 8 | it 'should run a shared example group and pass if all is well' do 9 | <<-describe_block.should pass_as_describe_block 10 | shared_examples_for "a passing spec" do 11 | it 'should pass' do 12 | end 13 | end 14 | 15 | describe "passing" do 16 | it_should_act_like "a passing spec" 17 | end 18 | describe_block 19 | end 20 | 21 | it 'should run a shared example group and pass if something is wrong' do 22 | <<-describe_block.should fail_as_describe_block 23 | shared_examples_for "a failing spec" do 24 | it 'should fail' do 25 | 0.should == 1 26 | end 27 | end 28 | 29 | describe "failing" do 30 | it_should_act_like "a failing spec" 31 | end 32 | describe_block 33 | end 34 | 35 | it 'should scope the reused examples so definitions of #let and #subject do not bleed into the calling example group' do 36 | 37 | <<-describe_block.should pass_as_describe_block 38 | shared_examples_for "a spec that lets something" do 39 | let(:foo) { "foo" } 40 | end 41 | 42 | describe "scoping" do 43 | it_should_act_like "a spec that lets something" 44 | 45 | let(:foo) { "bar" } 46 | 47 | it_should_act_like "a spec that lets something" 48 | 49 | it 'should not be affected by the previous it_should_act_like' do 50 | foo.should == "bar" 51 | end 52 | end 53 | describe_block 54 | 55 | end 56 | 57 | it 'should allow to parametrize the reused example group by calling it with a hash, whose keys will become #let variables' do 58 | 59 | shared_examples = <<-shared_examples 60 | shared_examples_for "a spec passing foo and bar" do 61 | it 'should see foo=1' do 62 | foo.should == 1 63 | end 64 | 65 | it 'should see bar=2' do 66 | bar.should == 2 67 | end 68 | end 69 | shared_examples 70 | 71 | <<-describe_block.should pass_as_describe_block 72 | 73 | #{shared_examples} 74 | 75 | describe "a spec passing the right params" do 76 | it_should_act_like "a spec passing foo and bar", :foo => 1, :bar => 2 77 | end 78 | 79 | describe_block 80 | 81 | <<-describe_block.should fail_as_describe_block 82 | 83 | #{shared_examples} 84 | 85 | describe "a spec passing the wrong params" do 86 | it_should_act_like "a spec passing foo and bar", :foo => 1, :bar => 3 87 | end 88 | 89 | describe_block 90 | end 91 | 92 | it 'should allow to parametrize the reused example group by calling it with a block, which will become a #let variable called "block"' do 93 | shared_examples = <<-shared_examples 94 | shared_examples_for "a spec passing a block" do 95 | it 'should get a block evaling as 1' do 96 | block.call.should == 1 97 | end 98 | end 99 | shared_examples 100 | 101 | <<-describe_block.should pass_as_describe_block 102 | #{shared_examples} 103 | 104 | describe "a spec passing the right block" do 105 | it_should_act_like "a spec passing a block" do 106 | 1 107 | end 108 | end 109 | describe_block 110 | 111 | <<-describe_block.should fail_as_describe_block 112 | #{shared_examples} 113 | 114 | describe "a spec passing the right block" do 115 | it_should_act_like "a spec passing a block" do 116 | 2 117 | end 118 | end 119 | describe_block 120 | end 121 | 122 | end 123 | 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This gem is no longer maintained! 2 | 3 | Most of the functionality of rspec_candy is now built into modern versions of [RSpec](http://rspec.info/). 4 | 5 | ------ 6 | 7 | rspec_candy [![Build Status](https://secure.travis-ci.org/makandra/rspec_candy.png?branch=master)](https://travis-ci.org/makandra/rspec_candy) 8 | ========================================== 9 | 10 | A collection of nifty helpers and matchers for your RSpec suite. 11 | 12 | Tested on: 13 | 14 | - RSpec 1 / Rails 2.3 15 | - RSpec 2 / Rails 3.2 16 | - RSpec 3 / Rails 4.2 17 | 18 | 19 | ## Installation 20 | 21 | Add `rspec_candy` to your Gemfile. 22 | 23 | Now, in your `spec_helper.rb`, add this after your RSpec requires: 24 | 25 | ```ruby 26 | require 'rspec_candy/all' 27 | ``` 28 | 29 | If you only care about the matchers or helpers you can also more specifically require: 30 | 31 | ```ruby 32 | require 'rspec_candy/matchers' 33 | require 'rspec_candy/helpers' 34 | ``` 35 | 36 | ## Matchers provided 37 | 38 | **be_same_number_as** 39 | 40 | Tests if the given number is the "same" as the receiving number, regardless of whether you're comparing `Fixnums` (integers), `Floats` and `BigDecimals`: 41 | 42 | ```ruby 43 | 100.should be_same_number_as(100.0) 44 | 50.4.should be_same_number_as(BigDecimal('50.4')) 45 | ``` 46 | 47 | Note that "same" means "same for your purposes". Internally the matcher compares normalized results of `#to_s`. 48 | 49 | **be_same_second_as** 50 | 51 | Tests if the given `Time` or `DateTime` is the same as the receiving `Time` or `DateTime`, [ignoring sub-second differences](https://makandracards.com/makandra/1057-why-two-ruby-time-objects-are-not-equal-although-they-appear-to-be): 52 | 53 | ```ruby 54 | Time.parse('2012-12-01 14:00:00.5').should == Time.parse('2012-12-01 14:00') 55 | ``` 56 | 57 | Note that two times in a different time zones will still be considered different. 58 | 59 | **include_hash** 60 | 61 | Matches if the given hash is included in the receiving hash: 62 | 63 | ```ruby 64 | { :foo => 'a', :bar => 'b' }.should include_hash(:foo => 'a') # passes 65 | { :foo => 'a', :bar => 'b' }.should include_hash(:foo => 'b') # fails 66 | { :foo => 'a', :bar => 'b' }.should include_hash(:foo => 'a', :baz => 'c') # fails 67 | ``` 68 | 69 | ## Helpers provided 70 | 71 | 72 | ### Extensions to **Object** 73 | 74 | **should_receive_and_execute** 75 | 76 | Like "should_receive", but also executes the method. 77 | 78 | 79 | **should_receive_and_return** 80 | 81 | Expects multiple returns at once: 82 | 83 | ```ruby 84 | dog.should_receive_and_return(:bark => "Woof", :fetch => "stick") 85 | ``` 86 | 87 | **should_receive_chain** 88 | 89 | Expect a chain of method calls: 90 | 91 | ```ruby 92 | dog.should_receive_chain(:bark, :upcase).and_return("WOOF") 93 | ``` 94 | 95 | You can also expect arguments, like: 96 | 97 | ```ruby 98 | dog.should_receive_chain([:bark, 'loudly'], :upcase).and_return("WOOF!!!") 99 | ``` 100 | 101 | **stub_existing** 102 | 103 | Like stub, but complains if the method did not exist in the first place. 104 | 105 | 106 | ### Extensions to **Class** 107 | 108 | **disposable_copy** 109 | 110 | Like dup for classes. This will temporarily add a method to a class: 111 | 112 | ```ruby 113 | copy = Model.disposable_copy do 114 | def foo; end 115 | end 116 | 117 | object = copy.new 118 | ``` 119 | 120 | **new_with_stubs** 121 | 122 | Instantiates and stubs in one call: 123 | 124 | ```ruby 125 | Model.new_with_stubs(:to_param => '1') 126 | ``` 127 | 128 | **stub_any_instance** 129 | 130 | Backport for `any_instance.stub` to RSpec1. 131 | 132 | 133 | ### Extensions to **example groups** 134 | 135 | **it_should_act_like** 136 | 137 | Extension to 'it_should_behave_like`, not necessary for RSpec2. 138 | 139 | Allows parametrizing shared examples, exposes parameters as lets: 140 | 141 | ```ruby 142 | shared_examples_for "an animal" do 143 | it 'should make noises' do 144 | subject.say.should == expected_noise 145 | end 146 | end 147 | 148 | describe Dog do 149 | it_should_act_like 'an animal', :expected_noise => 'Woof!' 150 | end 151 | ``` 152 | 153 | Blocks are passed as a let named "block". 154 | 155 | 156 | 157 | ### Extensions to **ActiveRecord::Base** 158 | 159 | **store_with_values** 160 | 161 | Only on Rails. 162 | 163 | Creates a record without validations or callbacks (like a stub in the database). Can be significantly faster than a factory. 164 | 165 | 166 | 167 | ##Changes from previous versions: 168 | 169 | - `new_and_store` has been renamed to `store_with_values` 170 | - `create_without_callbacks` has been renamed to `store_with_values` 171 | - `keep_invalid!` has been renamed to `prevent_storage` 172 | - `Object#should_not_receive_and_execute` has been removed (same as `Object#should_not_receive`) 173 | - `should_receive_all_with` (over-generic method name for a helper that is rarely useful) 174 | --------------------------------------------------------------------------------