├── Gemfile ├── lib ├── bourne.rb └── bourne │ ├── version.rb │ ├── mock.rb │ ├── invocation.rb │ ├── mockery.rb │ └── api.rb ├── .gitignore ├── .travis.yml ├── test ├── matcher_helpers.rb ├── simple_counter.rb ├── unit │ ├── invocation_test.rb │ ├── assert_received_test.rb │ ├── have_received_test.rb │ ├── mockery_test.rb │ └── mock_test.rb ├── method_definer.rb ├── execution_point.rb ├── test_helper.rb ├── acceptance │ ├── acceptance_test_helper.rb │ ├── stubba_example_test.rb │ ├── mocha_example_test.rb │ └── spy_test.rb ├── test_runner.rb └── mini_test_result.rb ├── NEWS.md ├── LICENSE ├── bourne.gemspec ├── Rakefile └── README.md /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /lib/bourne.rb: -------------------------------------------------------------------------------- 1 | require 'mocha/api' 2 | require 'bourne/api' 3 | -------------------------------------------------------------------------------- /lib/bourne/version.rb: -------------------------------------------------------------------------------- 1 | module Bourne 2 | VERSION = '1.5.0'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg 2 | *.swp 3 | .swo 4 | *~ 5 | tags 6 | rdoc 7 | *.gem 8 | Gemfile.lock 9 | .bundle 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 2.0.0 3 | - 1.9.3 4 | - 1.9.2 5 | - rbx-19mode 6 | 7 | matrix: 8 | allow_failures: 9 | - rvm: rbx-19mode 10 | -------------------------------------------------------------------------------- /test/matcher_helpers.rb: -------------------------------------------------------------------------------- 1 | class Test::Unit::TestCase 2 | def assert_matcher_accepts(matcher, instance) 3 | assert matcher.matches?(instance), matcher.failure_message 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/simple_counter.rb: -------------------------------------------------------------------------------- 1 | class SimpleCounter 2 | 3 | attr_reader :count 4 | 5 | def initialize 6 | @count = 0 7 | end 8 | 9 | def increment 10 | @count += 1 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /lib/bourne/mock.rb: -------------------------------------------------------------------------------- 1 | require 'mocha/mock' 2 | 3 | module Mocha # :nodoc: 4 | # Extends #method_missing on Mocha::Mock to record Invocations. 5 | class Mock # :nodoc: 6 | alias_method :method_missing_without_invocation, :method_missing 7 | 8 | def method_missing(symbol, *arguments, &block) 9 | Mockery.instance.invocation(self, symbol, arguments) 10 | method_missing_without_invocation(symbol, *arguments, &block) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | 1.3.2 (2013-02-15) 2 | * Compatible with Mocha 0.13.2 3 | 4 | 1.3.0 (2012-11-30) 5 | * Compatible with Mocha 0.13.0 6 | 7 | 1.2.1 (2012-11-26) 8 | * Compatible with Mocha 0.12.7 9 | 10 | 1.1.2 (2012-03-23) 11 | * Mention when a method is unstubbed 12 | * Compatible with Mocha 0.10.5 13 | 14 | 1.1.1 (2012-03-04) 15 | * Exactly the same as 1.1.0, but built with 1.8.7 so people with 1.8.7 can 16 | install the gem. 17 | 18 | 1.1.0 (2012-02-24) 19 | * 1.9 compatible 20 | -------------------------------------------------------------------------------- /test/unit/invocation_test.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), "..", "test_helper") 2 | require 'bourne/invocation' 3 | 4 | class InvocationTest < Test::Unit::TestCase 5 | 6 | include Mocha 7 | 8 | def test_has_mock_method_name_and_args 9 | mock = 'a mock' 10 | method = :call_me 11 | args = [1, 2] 12 | invocation = Invocation.new(mock, method, args) 13 | assert_equal mock, invocation.mock 14 | assert_equal method, invocation.method_name 15 | assert_equal args, invocation.arguments 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/bourne/invocation.rb: -------------------------------------------------------------------------------- 1 | module Mocha # :nodoc: 2 | # Used internally by Bourne extensions to Mocha. Represents a single 3 | # invocation of a stubbed or mocked method. The mock, method name, and 4 | # arguments are recorded and can be used to determine how a method was 5 | # invoked. 6 | class Invocation 7 | attr_reader :mock, :method_name, :arguments 8 | def initialize(mock, method_name, arguments) 9 | @mock = mock 10 | @method_name = method_name 11 | @arguments = arguments 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/bourne/mockery.rb: -------------------------------------------------------------------------------- 1 | require 'mocha/mockery' 2 | require 'bourne/mock' 3 | require 'bourne/invocation' 4 | 5 | module Mocha 6 | # Used internally by Bourne extensions to Mocha when recording invocations of 7 | # stubbed and mocked methods. You shouldn't need to interact with this module 8 | # through normal testing, but you can access the full list of invocations 9 | # directly if necessary. 10 | class Mockery 11 | def invocation(mock, method_name, args) 12 | invocations << Invocation.new(mock, method_name, args) 13 | end 14 | 15 | def invocations 16 | @invocations ||= [] 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/method_definer.rb: -------------------------------------------------------------------------------- 1 | require 'metaclass' 2 | 3 | module Mocha 4 | 5 | module ObjectMethods 6 | def define_instance_method(method_symbol, &block) 7 | __metaclass__.send(:define_method, method_symbol, block) 8 | end 9 | 10 | def replace_instance_method(method_symbol, &block) 11 | raise "Cannot replace #{method_symbol} as #{self} does not respond to it." unless self.respond_to?(method_symbol) 12 | define_instance_method(method_symbol, &block) 13 | end 14 | 15 | def define_instance_accessor(*symbols) 16 | symbols.each { |symbol| __metaclass__.send(:attr_accessor, symbol) } 17 | end 18 | end 19 | 20 | end 21 | 22 | class Object 23 | include Mocha::ObjectMethods 24 | end 25 | -------------------------------------------------------------------------------- /test/execution_point.rb: -------------------------------------------------------------------------------- 1 | class ExecutionPoint 2 | 3 | attr_reader :backtrace 4 | 5 | def self.current 6 | new(caller) 7 | end 8 | 9 | def initialize(backtrace) 10 | @backtrace = backtrace 11 | end 12 | 13 | def file_name 14 | return "unknown" unless @backtrace && @backtrace.first 15 | /\A(.*?):\d+/.match(@backtrace.first)[1] 16 | end 17 | 18 | def line_number 19 | return "unknown" unless @backtrace && @backtrace.first 20 | Integer(/\A.*?:(\d+)/.match(@backtrace.first)[1]) 21 | end 22 | 23 | def ==(other) 24 | return false unless other.is_a?(ExecutionPoint) 25 | (file_name == other.file_name) and (line_number == other.line_number) 26 | end 27 | 28 | def to_s 29 | "file: #{file_name}; line: #{line_number}" 30 | end 31 | 32 | def inspect 33 | to_s 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | unless defined?(STANDARD_OBJECT_PUBLIC_INSTANCE_METHODS) 2 | STANDARD_OBJECT_PUBLIC_INSTANCE_METHODS = Object.public_instance_methods 3 | end 4 | 5 | $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")) 6 | $:.unshift File.expand_path(File.join(File.dirname(__FILE__))) 7 | $:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'unit')) 8 | $:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'unit', 'parameter_matchers')) 9 | $:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'acceptance')) 10 | 11 | if ENV['MOCHA_OPTIONS'] == 'use_test_unit_gem' 12 | require 'rubygems' 13 | gem 'test-unit' 14 | end 15 | 16 | require 'test/unit' 17 | require 'mocha/setup' 18 | 19 | if defined?(MiniTest) 20 | FailedAssertion = MiniTest::Assertion 21 | else 22 | FailedAssertion = Test::Unit::AssertionFailedError 23 | end 24 | -------------------------------------------------------------------------------- /test/acceptance/acceptance_test_helper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../test_helper', __FILE__) 2 | require 'test_runner' 3 | require 'mocha/configuration' 4 | 5 | module AcceptanceTest 6 | 7 | class FakeLogger 8 | 9 | attr_reader :warnings 10 | 11 | def initialize 12 | @warnings = [] 13 | end 14 | 15 | def warn(message) 16 | @warnings << message 17 | end 18 | 19 | end 20 | 21 | attr_reader :logger 22 | 23 | include TestRunner 24 | 25 | def setup_acceptance_test 26 | Mocha::Configuration.reset_configuration 27 | @logger = FakeLogger.new 28 | mockery = Mocha::Mockery.instance 29 | @original_logger = mockery.logger 30 | mockery.logger = @logger 31 | end 32 | 33 | def teardown_acceptance_test 34 | Mocha::Configuration.reset_configuration 35 | Mocha::Mockery.instance.logger = @original_logger 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2013 Joe Ferris and thoughtbot, inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /bourne.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "bourne/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'bourne' 7 | s.version = Bourne::VERSION.dup 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ["Joe Ferris"] 10 | s.email = 'jferris@thoughtbot.com' 11 | s.homepage = 'http://github.com/thoughtbot/bourne' 12 | s.summary = 'Adds test spies to mocha.' 13 | s.description = %q{Extends mocha to allow detailed tracking and querying of 14 | stub and mock invocations. Allows test spies using the have_received rspec 15 | matcher and assert_received for Test::Unit. Extracted from the 16 | jferris-mocha fork.} 17 | 18 | s.files = `git ls-files`.split("\n") 19 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 20 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 21 | s.require_paths = ["lib"] 22 | 23 | # Follow instructions in test/unit/mock_test.rb to update. 24 | s.add_dependency('mocha', '>= 0.13.2', '< 0.15') 25 | 26 | s.add_development_dependency('rake') 27 | end 28 | -------------------------------------------------------------------------------- /test/test_runner.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit/testcase' 2 | 3 | if defined?(MiniTest) 4 | require 'mocha/integration/mini_test' 5 | require File.expand_path('../mini_test_result', __FILE__) 6 | else 7 | require 'test/unit/testresult' 8 | end 9 | 10 | module TestRunner 11 | def run_as_test(test_result = nil, &block) 12 | test_class = Class.new(Test::Unit::TestCase) do 13 | define_method(:test_me, &block) 14 | end 15 | test = test_class.new(:test_me) 16 | 17 | if defined?(Test::Unit::TestResult) 18 | test_result ||= Test::Unit::TestResult.new 19 | test.run(test_result) {} 20 | class << test_result 21 | attr_reader :failures, :errors 22 | def failure_messages 23 | failures.map { |failure| failure.message } 24 | end 25 | def failure_message_lines 26 | failure_messages.map { |message| message.split("\n") }.flatten 27 | end 28 | def error_messages 29 | errors.map { |error| error.message } 30 | end 31 | end 32 | else 33 | runner = MiniTest::Unit.new 34 | test.run(runner) 35 | test_result = MiniTestResult.new(runner, test) 36 | end 37 | 38 | test_result 39 | end 40 | 41 | def assert_passed(test_result) 42 | flunk "Test failed unexpectedly with message: #{test_result.failures}" if test_result.failure_count > 0 43 | flunk "Test failed unexpectedly with message: #{test_result.errors}" if test_result.error_count > 0 44 | end 45 | 46 | def assert_failed(test_result) 47 | flunk "Test passed unexpectedly" if test_result.passed? 48 | end 49 | 50 | end 51 | 52 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'bundler/gem_tasks' 3 | require 'rake/testtask' 4 | 5 | desc "Run all tests" 6 | task 'default' => ['test:units', 'test:acceptance', 'test:performance'] 7 | 8 | namespace 'test' do 9 | unit_tests = FileList['test/unit/**/*_test.rb'] 10 | acceptance_tests = FileList['test/acceptance/*_test.rb'] 11 | 12 | desc "Run unit tests" 13 | Rake::TestTask.new('units') do |t| 14 | t.libs << 'test' 15 | t.test_files = unit_tests 16 | end 17 | 18 | desc "Run acceptance tests" 19 | Rake::TestTask.new('acceptance') do |t| 20 | t.libs << 'test' 21 | t.test_files = acceptance_tests 22 | end 23 | 24 | desc "Run performance tests" 25 | task 'performance' do 26 | require File.join(File.dirname(__FILE__), 'test', 'acceptance', 'stubba_example_test') 27 | require File.join(File.dirname(__FILE__), 'test', 'acceptance', 'mocha_example_test') 28 | iterations = 1000 29 | puts "\nBenchmarking with #{iterations} iterations..." 30 | [MochaExampleTest, StubbaExampleTest].each do |test_case| 31 | puts "#{test_case}: #{benchmark_test_case(test_case, iterations)} seconds." 32 | end 33 | end 34 | end 35 | 36 | def benchmark_test_case(klass, iterations) 37 | require 'benchmark' 38 | 39 | if defined?(MiniTest) 40 | MiniTest::Unit.output = StringIO.new 41 | Benchmark.realtime { iterations.times { |i| MiniTest::Unit.new.run([klass]) } } 42 | else 43 | load 'test/unit/ui/console/testrunner.rb' unless defined?(Test::Unit::UI::Console::TestRunner) 44 | unless $silent_option 45 | begin 46 | load 'test/unit/ui/console/outputlevel.rb' unless defined?(Test::Unit::UI::Console::OutputLevel::SILENT) 47 | $silent_option = { :output_level => Test::Unit::UI::Console::OutputLevel::SILENT } 48 | rescue LoadError 49 | $silent_option = Test::Unit::UI::SILENT 50 | end 51 | end 52 | Benchmark.realtime { iterations.times { Test::Unit::UI::Console::TestRunner.run(klass, $silent_option) } } 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /test/mini_test_result.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | require 'test/unit/testcase' 3 | require 'minitest/unit' 4 | 5 | class MiniTestResult 6 | 7 | FAILURE_PATTERN = %r{(Failure)\:\n([^\(]+)\(([^\)]+)\) \[([^\]]+)\]\:\n(.*)\n}m 8 | ERROR_PATTERN = %r{(Error)\:\n([^\(]+)\(([^\)]+)\)\:\n(.+?)\n}m 9 | 10 | def self.parse_failure(raw) 11 | matches = FAILURE_PATTERN.match(raw) 12 | return nil unless matches 13 | Failure.new(matches[2], matches[3], [matches[4]], matches[5]) 14 | end 15 | 16 | def self.parse_error(raw) 17 | matches = ERROR_PATTERN.match(raw) 18 | return nil unless matches 19 | backtrace = raw.gsub(ERROR_PATTERN, '').split("\n").map(&:strip) 20 | Error.new(matches[2], matches[3], matches[4], backtrace) 21 | end 22 | 23 | class Failure 24 | attr_reader :method, :test_case, :location, :message 25 | def initialize(method, test_case, location, message) 26 | @method, @test_case, @location, @message = method, test_case, location, message 27 | end 28 | end 29 | 30 | class Error 31 | class Exception 32 | attr_reader :message, :backtrace 33 | def initialize(message, location) 34 | @message, @backtrace = message, location 35 | end 36 | end 37 | 38 | attr_reader :method, :test_case, :exception 39 | def initialize(method, test_case, message, backtrace) 40 | @method, @test_case, @exception = method, test_case, Exception.new(message, backtrace) 41 | end 42 | end 43 | 44 | def initialize(runner, test) 45 | @runner, @test = runner, test 46 | end 47 | 48 | def failure_count 49 | @runner.failures 50 | end 51 | 52 | def assertion_count 53 | @test._assertions 54 | end 55 | 56 | def error_count 57 | @runner.errors 58 | end 59 | 60 | def passed? 61 | @test.passed? 62 | end 63 | 64 | def failures 65 | @runner.report.map { |puked| MiniTestResult.parse_failure(puked) }.compact 66 | end 67 | 68 | def errors 69 | @runner.report.map { |puked| MiniTestResult.parse_error(puked) }.compact 70 | end 71 | 72 | def failure_messages 73 | failures.map(&:message) 74 | end 75 | 76 | def failure_message_lines 77 | failure_messages.map { |message| message.split("\n") }.flatten 78 | end 79 | 80 | def error_messages 81 | errors.map { |e| e.exception.message } 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Bourne [![Build Status](https://secure.travis-ci.org/thoughtbot/bourne.png?branch=master)](http://travis-ci.org/thoughtbot/bourne) 2 | ====== 3 | 4 | Bourne extends mocha to allow detailed tracking and querying of stub and mock 5 | invocations. It allows test spies using the have_received rspec matcher and 6 | assert_received for Test::Unit. Bourne was extracted from jferris-mocha, a fork 7 | of mocha that adds test spies. 8 | 9 | Test Spies 10 | ---------- 11 | 12 | Test spies are a form of test double that preserves the normal four-phase unit 13 | test order and allows separation of stubbing and verification. 14 | 15 | Using a test spy is like using a mocked expectation except that there are two steps: 16 | 17 | 1. Stub out a method for which you want to verify invocations 18 | 2. Use an assertion or matcher to verify that the stub was invoked correctly 19 | 20 | Examples 21 | -------- 22 | 23 | RSpec: 24 | 25 | mock.should have_received(:to_s) 26 | Radio.should have_received(:new).with(1041) 27 | radio.should have_received(:volume).with(11).twice 28 | radio.should have_received(:off).never 29 | 30 | You also want to configure RSpec to use mocha for mocking: 31 | 32 | RSpec.configure do |config| 33 | config.mock_with :mocha 34 | end 35 | 36 | Test::Unit: 37 | 38 | assert_received(mock, :to_s) 39 | assert_received(Radio, :new) {|expect| expect.with(1041) } 40 | assert_received(radio, :volume) {|expect| expect.with(11).twice } 41 | assert_received(radio, :off) {|expect| expect.never } 42 | 43 | See Mocha::API for more information. 44 | 45 | Install 46 | ------- 47 | 48 | gem install bourne 49 | 50 | More Information 51 | ---------------- 52 | 53 | * [RDoc](http://rdoc.info/projects/thoughtbot/bourne) 54 | * [Issues](http://github.com/thoughtbot/bourne/issues) 55 | * [Mocha mailing list](http://groups.google.com/group/mocha-developer) 56 | 57 | Credits 58 | ------- 59 | 60 | Bourne was written by Joe Ferris. Mocha was written by James Mead. Several of 61 | the test examples and helpers used in the Bourne test suite were copied 62 | directly from Mocha. 63 | 64 | Thanks to thoughtbot for inspiration, ideas, and funding. Thanks to James for 65 | writing mocha. 66 | 67 | ![thoughtbot](http://thoughtbot.com/images/tm/logo.png) 68 | 69 | Thank you to all [the contributors](https://github.com/thoughtbot/bourne/contributors)! 70 | 71 | The names and logos for thoughtbot are trademarks of thoughtbot, inc. 72 | 73 | License 74 | ------- 75 | 76 | Bourne is Copyright © 2010-2013 Joe Ferris and thoughtbot. It is free software, and may be redistributed under the terms specified in the LICENSE file. 77 | -------------------------------------------------------------------------------- /test/acceptance/stubba_example_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../test_helper', __FILE__) 2 | require 'mocha/setup' 3 | 4 | class Widget 5 | 6 | def model 7 | 'original_model' 8 | end 9 | 10 | class << self 11 | 12 | def find(options) 13 | [] 14 | end 15 | 16 | def create(attributes) 17 | Widget.new 18 | end 19 | 20 | end 21 | 22 | end 23 | 24 | module Thingy 25 | 26 | def self.wotsit 27 | :hoojamaflip 28 | end 29 | 30 | end 31 | 32 | class StubbaExampleTest < Test::Unit::TestCase 33 | 34 | def test_should_stub_instance_method 35 | widget = Widget.new 36 | widget.expects(:model).returns('different_model') 37 | assert_equal 'different_model', widget.model 38 | end 39 | 40 | def test_should_stub_module_method 41 | should_stub_module_method 42 | end 43 | 44 | def test_should_stub_module_method_again 45 | should_stub_module_method 46 | end 47 | 48 | def test_should_stub_class_method 49 | should_stub_class_method 50 | end 51 | 52 | def test_should_stub_class_method_again 53 | should_stub_class_method 54 | end 55 | 56 | def test_should_stub_instance_method_on_any_instance_of_a_class 57 | should_stub_instance_method_on_any_instance_of_a_class 58 | end 59 | 60 | def test_should_stub_instance_method_on_any_instance_of_a_class_again 61 | should_stub_instance_method_on_any_instance_of_a_class 62 | end 63 | 64 | def test_should_stub_two_different_class_methods 65 | should_stub_two_different_class_methods 66 | end 67 | 68 | def test_should_stub_two_different_class_methods_again 69 | should_stub_two_different_class_methods 70 | end 71 | 72 | private 73 | 74 | def should_stub_module_method 75 | Thingy.expects(:wotsit).returns(:dooda) 76 | assert_equal :dooda, Thingy.wotsit 77 | end 78 | 79 | def should_stub_class_method 80 | widgets = [Widget.new] 81 | Widget.expects(:find).with(:all).returns(widgets) 82 | assert_equal widgets, Widget.find(:all) 83 | end 84 | 85 | def should_stub_two_different_class_methods 86 | found_widgets = [Widget.new] 87 | created_widget = Widget.new 88 | Widget.expects(:find).with(:all).returns(found_widgets) 89 | Widget.expects(:create).with(:model => 'wombat').returns(created_widget) 90 | assert_equal found_widgets, Widget.find(:all) 91 | assert_equal created_widget, Widget.create(:model => 'wombat') 92 | end 93 | 94 | def should_stub_instance_method_on_any_instance_of_a_class 95 | Widget.any_instance.expects(:model).at_least_once.returns('another_model') 96 | widget_1 = Widget.new 97 | widget_2 = Widget.new 98 | assert_equal 'another_model', widget_1.model 99 | assert_equal 'another_model', widget_2.model 100 | end 101 | 102 | end 103 | -------------------------------------------------------------------------------- /test/acceptance/mocha_example_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../test_helper', __FILE__) 2 | require 'mocha/setup' 3 | 4 | class MochaExampleTest < Test::Unit::TestCase 5 | 6 | class Rover 7 | 8 | def initialize(left_track, right_track, steps_per_metre, steps_per_degree) 9 | @left_track, @right_track, @steps_per_metre, @steps_per_degree = left_track, right_track, steps_per_metre, steps_per_degree 10 | end 11 | 12 | def forward(metres) 13 | @left_track.step(metres * @steps_per_metre) 14 | @right_track.step(metres * @steps_per_metre) 15 | wait 16 | end 17 | 18 | def backward(metres) 19 | forward(-metres) 20 | end 21 | 22 | def left(degrees) 23 | @left_track.step(-degrees * @steps_per_degree) 24 | @right_track.step(+degrees * @steps_per_degree) 25 | wait 26 | end 27 | 28 | def right(degrees) 29 | left(-degrees) 30 | end 31 | 32 | def wait 33 | while (@left_track.moving? or @right_track.moving?); end 34 | end 35 | 36 | end 37 | 38 | def test_should_step_both_tracks_forward_ten_steps 39 | left_track = mock('left_track') 40 | right_track = mock('right_track') 41 | steps_per_metre = 5 42 | rover = Rover.new(left_track, right_track, steps_per_metre, nil) 43 | 44 | left_track.expects(:step).with(10) 45 | right_track.expects(:step).with(10) 46 | 47 | left_track.stubs(:moving?).returns(false) 48 | right_track.stubs(:moving?).returns(false) 49 | 50 | rover.forward(2) 51 | end 52 | 53 | def test_should_step_both_tracks_backward_ten_steps 54 | left_track = mock('left_track') 55 | right_track = mock('right_track') 56 | steps_per_metre = 5 57 | rover = Rover.new(left_track, right_track, steps_per_metre, nil) 58 | 59 | left_track.expects(:step).with(-10) 60 | right_track.expects(:step).with(-10) 61 | 62 | left_track.stubs(:moving?).returns(false) 63 | right_track.stubs(:moving?).returns(false) 64 | 65 | rover.backward(2) 66 | end 67 | 68 | def test_should_step_left_track_forwards_five_steps_and_right_track_backwards_five_steps 69 | left_track = mock('left_track') 70 | right_track = mock('right_track') 71 | steps_per_degree = 5.0 / 90.0 72 | rover = Rover.new(left_track, right_track, nil, steps_per_degree) 73 | 74 | left_track.expects(:step).with(+5) 75 | right_track.expects(:step).with(-5) 76 | 77 | left_track.stubs(:moving?).returns(false) 78 | right_track.stubs(:moving?).returns(false) 79 | 80 | rover.right(90) 81 | end 82 | 83 | def test_should_step_left_track_backwards_five_steps_and_right_track_forwards_five_steps 84 | left_track = mock('left_track') 85 | right_track = mock('right_track') 86 | steps_per_degree = 5.0 / 90.0 87 | rover = Rover.new(left_track, right_track, nil, steps_per_degree) 88 | 89 | left_track.expects(:step).with(-5) 90 | right_track.expects(:step).with(+5) 91 | 92 | left_track.stubs(:moving?).returns(false) 93 | right_track.stubs(:moving?).returns(false) 94 | 95 | rover.left(90) 96 | end 97 | 98 | end 99 | -------------------------------------------------------------------------------- /lib/bourne/api.rb: -------------------------------------------------------------------------------- 1 | require 'mocha/api' 2 | require 'bourne/mockery' 3 | 4 | module Mocha # :nodoc: 5 | module API 6 | # Asserts that the given mock received the given method. 7 | # 8 | # Examples: 9 | # 10 | # assert_received(mock, :to_s) 11 | # assert_received(Radio, :new) {|expect| expect.with(1041) } 12 | # assert_received(radio, :volume) {|expect| expect.with(11).twice } 13 | def assert_received(mock, expected_method_name) 14 | matcher = have_received(expected_method_name) 15 | yield(matcher) if block_given? 16 | assert matcher.matches?(mock), matcher.failure_message 17 | end 18 | 19 | class InvalidHaveReceived < StandardError 20 | end 21 | 22 | class HaveReceived #:nodoc: 23 | def initialize(expected_method_name) 24 | @expected_method_name = expected_method_name 25 | @expectations = [] 26 | end 27 | 28 | def method_missing(method, *args, &block) 29 | @expectations << [method, args, block] 30 | self 31 | end 32 | 33 | def matches?(mock) 34 | if mock.respond_to?(:mocha) 35 | @mock = mock.mocha 36 | else 37 | @mock = mock 38 | end 39 | 40 | @expectation = Expectation.new(@mock, @expected_method_name) 41 | @expectations.each do |method, args, block| 42 | @expectation.send(method, *args, &block) 43 | end 44 | invocation_count.times { @expectation.invoke } 45 | @expectation.verified? 46 | end 47 | 48 | def does_not_match?(mock) 49 | raise InvalidHaveReceived.new("should_not have_received(:#{@expected_method_name}) is invalid, please use" + 50 | " should have_received(:#{@expected_method_name}).never") 51 | end 52 | 53 | def failure_message 54 | message = "" 55 | if matching_stubs.empty? 56 | message << "unstubbed, " 57 | end 58 | message << @expectation.mocha_inspect 59 | end 60 | 61 | private 62 | 63 | def invocation_count 64 | matching_invocations.size 65 | end 66 | 67 | def matching_invocations 68 | invocations.select do |invocation| 69 | @expectation.match?(invocation.method_name, *invocation.arguments) 70 | end 71 | end 72 | 73 | def invocations 74 | Mockery.instance.invocations.select do |invocation| 75 | invocation.mock.equal?(@mock) || invocation.mock.mocha.equal?(@mock) 76 | end 77 | end 78 | 79 | def matching_stubs 80 | Mockery.instance.stubba.stubba_methods.select do |method| 81 | matching_stub?(method) 82 | end 83 | end 84 | 85 | def matching_stub?(method) 86 | method.mock.equal?(@mock) && method.method == @expected_method_name 87 | end 88 | end 89 | 90 | # :call-seq: 91 | # should have_received(method).with(arguments).times(times) 92 | # 93 | # Ensures that the given mock received the given method. 94 | # 95 | # Examples: 96 | # 97 | # mock.should have_received(:to_s) 98 | # Radio.should have_received(:new).with(1041) 99 | # radio.should have_received(:volume).with(11).twice 100 | def have_received(expected_method_name) 101 | HaveReceived.new(expected_method_name) 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /test/unit/assert_received_test.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), "..", "test_helper") 2 | require 'test_runner' 3 | require 'bourne/api' 4 | require 'bourne/mockery' 5 | require 'mocha/api' 6 | 7 | class AssertReceivedTest < Test::Unit::TestCase 8 | 9 | include Mocha 10 | include TestRunner 11 | include Mocha::API 12 | 13 | def teardown 14 | Mockery.reset_instance 15 | end 16 | 17 | class FakeMock 18 | def initialize(name) 19 | @name = name 20 | end 21 | 22 | def inspect 23 | @name 24 | end 25 | 26 | def mocha 27 | self 28 | end 29 | end 30 | 31 | def test_passes_if_invocation_exists 32 | method = :a_method 33 | mock = FakeMock.new('a mock') 34 | Mockery.instance.invocation(mock, method, []) 35 | assert_passes do 36 | assert_received(mock, method) 37 | end 38 | end 39 | 40 | def test_fails_if_invocation_doesnt_exist 41 | method = :a_method 42 | mock = FakeMock.new('a mock') 43 | assert_fails do 44 | assert_received(mock, method) 45 | end 46 | end 47 | 48 | def test_fails_if_invocation_exists_with_different_arguments 49 | method = :a_method 50 | mock = FakeMock.new('a mock') 51 | Mockery.instance.invocation(mock, method, [2, 1]) 52 | assert_fails do 53 | assert_received(mock, method) {|expect| expect.with(1, 2) } 54 | end 55 | end 56 | 57 | def test_passes_if_invocation_exists_with_wildcard_arguments 58 | method = :a_method 59 | mock = FakeMock.new('a mock') 60 | Mockery.instance.invocation(mock, method, ['hello']) 61 | assert_passes do 62 | assert_received(mock, method) {|expect| expect.with(is_a(String)) } 63 | end 64 | end 65 | 66 | def test_passes_if_invocation_exists_with_exact_arguments 67 | method = :a_method 68 | mock = FakeMock.new('a mock') 69 | Mockery.instance.invocation(mock, method, ['hello']) 70 | assert_passes do 71 | assert_received(mock, method) {|expect| expect.with('hello') } 72 | end 73 | end 74 | 75 | def test_fails_if_invocation_exists_only_on_other_mock 76 | method = :a_method 77 | mock = FakeMock.new('a mock') 78 | other = 'another mock' 79 | Mockery.instance.invocation(other, method, ['hello']) 80 | assert_fails do 81 | assert_received(mock, method) 82 | end 83 | end 84 | 85 | def test_passes_if_invocation_exists_for_impersonating_mock 86 | method = :a_method 87 | object = Object.new 88 | mock = FakeMock.new('a mock') 89 | 90 | class << object 91 | attr_accessor :mocha 92 | end 93 | object.mocha = mock 94 | 95 | Mockery.instance.invocation(mock, method, ['hello']) 96 | assert_passes do 97 | assert_received(object, method) {|expect| expect.with('hello') } 98 | end 99 | end 100 | 101 | def test_passes_if_invocation_count_correct 102 | method = :a_method 103 | mock = FakeMock.new('a mock') 104 | 2.times { Mockery.instance.invocation(mock, method, []) } 105 | assert_passes do 106 | assert_received(mock, method) {|expect| expect.twice } 107 | end 108 | end 109 | 110 | def test_fails_if_invocation_count_too_low 111 | method = :a_method 112 | mock = FakeMock.new('a mock') 113 | Mockery.instance.invocation(mock, method, []) 114 | assert_fails do 115 | assert_received(mock, method) {|expect| expect.twice } 116 | end 117 | end 118 | 119 | def test_fails_if_invocation_count_too_high 120 | method = :a_method 121 | mock = FakeMock.new('a mock') 122 | 2.times { Mockery.instance.invocation(mock, method, []) } 123 | assert_fails do 124 | assert_received(mock, method) {|expect| expect.once } 125 | end 126 | end 127 | 128 | def assert_passes(&block) 129 | assert ! fails?(&block) 130 | end 131 | 132 | def assert_fails(&block) 133 | assert fails?(&block) 134 | end 135 | 136 | def fails? 137 | begin 138 | yield 139 | false 140 | rescue FailedAssertion 141 | true 142 | end 143 | end 144 | 145 | end 146 | -------------------------------------------------------------------------------- /test/unit/have_received_test.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), "..", "test_helper") 2 | require 'test_runner' 3 | require 'bourne/api' 4 | require 'bourne/mockery' 5 | require 'mocha/api' 6 | require 'matcher_helpers' 7 | 8 | module HaveReceivedTestMethods 9 | 10 | include Mocha 11 | 12 | def teardown 13 | Mockery.reset_instance 14 | end 15 | 16 | def test_passes_if_invocation_exists 17 | method = :a_method 18 | mock = new_mock('a mock') 19 | Mockery.instance.invocation(mock, method, []) 20 | assert_passes do 21 | assert_matcher_accepts have_received(method), mock 22 | end 23 | end 24 | 25 | def test_fails_if_invocation_doesnt_exist 26 | method = :a_method 27 | mock = new_mock('a mock') 28 | assert_fails do 29 | assert_matcher_accepts have_received(method), mock 30 | end 31 | end 32 | 33 | def test_fails_if_invocation_exists_with_different_arguments 34 | method = :a_method 35 | mock = new_mock('a mock') 36 | Mockery.instance.invocation(mock, method, [2, 1]) 37 | assert_fails do 38 | assert_matcher_accepts have_received(method).with(1, 2), mock 39 | end 40 | end 41 | 42 | def test_passes_if_invocation_exists_with_wildcard_arguments 43 | method = :a_method 44 | mock = new_mock('a mock') 45 | Mockery.instance.invocation(mock, method, ['hello']) 46 | assert_passes do 47 | assert_matcher_accepts have_received(method).with(is_a(String)), mock 48 | end 49 | end 50 | 51 | def test_passes_if_invocation_exists_with_exact_arguments 52 | method = :a_method 53 | mock = new_mock('a mock') 54 | Mockery.instance.invocation(mock, method, ['hello']) 55 | assert_passes do 56 | assert_matcher_accepts have_received(method).with('hello'), mock 57 | end 58 | end 59 | 60 | def test_fails_if_invocation_exists_only_on_other_mock 61 | method = :a_method 62 | mock = new_mock('a mock') 63 | other = 'another mock' 64 | Mockery.instance.invocation(other, method, ['hello']) 65 | assert_fails do 66 | assert_matcher_accepts have_received(method), mock 67 | end 68 | end 69 | 70 | def test_passes_if_invocation_exists_for_impersonating_mock 71 | method = :a_method 72 | object = Object.new 73 | mock = new_mock('a mock') 74 | 75 | class << object 76 | attr_accessor :mocha 77 | end 78 | object.mocha = mock 79 | 80 | Mockery.instance.invocation(mock, method, ['hello']) 81 | assert_passes do 82 | assert_matcher_accepts have_received(method).with('hello'), object 83 | end 84 | end 85 | 86 | def test_passes_if_invocation_count_correct 87 | method = :a_method 88 | mock = new_mock('a mock') 89 | 2.times { Mockery.instance.invocation(mock, method, []) } 90 | assert_passes do 91 | assert_matcher_accepts have_received(method).twice, mock 92 | end 93 | end 94 | 95 | def test_fails_if_invocation_count_incorrect 96 | method = :a_method 97 | mock = new_mock('a mock') 98 | Mockery.instance.invocation(mock, method, []) 99 | assert_fails do 100 | assert_matcher_accepts have_received(method).twice, mock 101 | end 102 | end 103 | 104 | def test_fails_if_invocation_count_too_low 105 | method = :a_method 106 | mock = new_mock('a mock') 107 | Mockery.instance.invocation(mock, method, []) 108 | assert_fails do 109 | assert_matcher_accepts have_received(method).twice, mock 110 | end 111 | end 112 | 113 | def test_fails_if_invocation_count_too_high 114 | method = :a_method 115 | mock = new_mock('a mock') 116 | 2.times { Mockery.instance.invocation(mock, method, []) } 117 | assert_fails do 118 | assert_matcher_accepts have_received(method).once, mock 119 | end 120 | end 121 | 122 | def assert_passes(&block) 123 | assert ! fails?(&block) 124 | end 125 | 126 | def assert_fails(&block) 127 | assert fails?(&block) 128 | end 129 | 130 | def fails? 131 | begin 132 | yield 133 | false 134 | rescue FailedAssertion 135 | true 136 | end 137 | end 138 | 139 | end 140 | 141 | class PartialHaveReceivedTest < Test::Unit::TestCase 142 | 143 | include TestRunner 144 | include Mocha::API 145 | include HaveReceivedTestMethods 146 | 147 | class FakeMock 148 | def initialize(name) 149 | @name = name 150 | end 151 | 152 | def inspect 153 | @name 154 | end 155 | 156 | def mocha 157 | self 158 | end 159 | end 160 | 161 | def new_mock(*args) 162 | FakeMock.new(*args) 163 | end 164 | 165 | end 166 | 167 | 168 | class PureHaveReceivedTest < Test::Unit::TestCase 169 | 170 | include TestRunner 171 | include Mocha::API 172 | include HaveReceivedTestMethods 173 | 174 | class FakeMock 175 | def initialize(name) 176 | @name = name 177 | end 178 | 179 | def inspect 180 | @name 181 | end 182 | 183 | def mocha 184 | self 185 | end 186 | end 187 | 188 | def new_mock(*args) 189 | Mocha::Mock.new(*args) 190 | end 191 | 192 | end 193 | -------------------------------------------------------------------------------- /test/acceptance/spy_test.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), "acceptance_test_helper") 2 | require 'bourne' 3 | require 'matcher_helpers' 4 | 5 | module SpyTestMethods 6 | 7 | def setup 8 | setup_acceptance_test 9 | end 10 | 11 | def teardown 12 | teardown_acceptance_test 13 | end 14 | 15 | def test_should_accept_wildcard_stub_call_without_arguments 16 | instance = new_instance 17 | instance.stubs(:magic) 18 | instance.magic 19 | assert_received(instance, :magic) 20 | assert_matcher_accepts have_received(:magic), instance 21 | end 22 | 23 | def test_should_accept_wildcard_stub_call_with_arguments 24 | instance = new_instance 25 | instance.stubs(:magic) 26 | instance.magic(:argument) 27 | assert_received(instance, :magic) 28 | assert_matcher_accepts have_received(:magic), instance 29 | end 30 | 31 | def test_should_not_accept_wildcard_stub_without_call 32 | instance = new_instance 33 | instance.stubs(:magic) 34 | assert_fails { assert_received(instance, :magic) } 35 | assert_fails { assert_matcher_accepts have_received(:magic), instance } 36 | end 37 | 38 | def test_should_not_accept_call_without_arguments 39 | instance = new_instance 40 | instance.stubs(:magic) 41 | instance.magic 42 | assert_fails { assert_received(instance, :magic) {|expect| expect.with(1) } } 43 | assert_fails { assert_matcher_accepts have_received(:magic).with(1), instance } 44 | end 45 | 46 | def test_should_not_accept_call_with_different_arguments 47 | instance = new_instance 48 | instance.stubs(:magic) 49 | instance.magic(2) 50 | assert_fails { assert_received(instance, :magic) {|expect| expect.with(1) } } 51 | assert_fails { assert_matcher_accepts have_received(:magic).with(1), instance } 52 | end 53 | 54 | def test_should_accept_call_with_correct_arguments 55 | instance = new_instance 56 | instance.stubs(:magic) 57 | instance.magic(1) 58 | assert_received(instance, :magic) {|expect| expect.with(1) } 59 | assert_matcher_accepts have_received(:magic).with(1), instance 60 | end 61 | 62 | def test_should_accept_call_with_wildcard_arguments 63 | instance = new_instance 64 | instance.stubs(:magic) 65 | instance.magic('hello') 66 | assert_received(instance, :magic) {|expect| expect.with(is_a(String)) } 67 | assert_matcher_accepts have_received(:magic).with(is_a(String)), instance 68 | end 69 | 70 | def test_should_reject_call_on_different_mock 71 | instance = new_instance 72 | other = new_instance 73 | instance.stubs(:magic) 74 | other.stubs(:magic) 75 | other.magic('hello') 76 | assert_fails { assert_received(instance, :magic) {|expect| expect.with(is_a(String)) } } 77 | assert_fails { assert_matcher_accepts have_received(:magic).with(is_a(String)), instance } 78 | end 79 | 80 | def test_should_accept_correct_number_of_calls 81 | instance = new_instance 82 | instance.stubs(:magic) 83 | 2.times { instance.magic } 84 | assert_received(instance, :magic) {|expect| expect.twice } 85 | assert_matcher_accepts have_received(:magic).twice, instance 86 | end 87 | 88 | def test_should_not_allow_should_not 89 | begin 90 | have_received(:magic).does_not_match?(new_instance) 91 | rescue Mocha::API::InvalidHaveReceived => exception 92 | assert_match "should_not have_received(:magic) is invalid, please use should have_received(:magic).never", exception.message, "Test failed, but with the wrong message" 93 | return 94 | end 95 | flunk("Expected to fail") 96 | end 97 | 98 | def test_should_warn_for_unstubbed_methods_with_expectations 99 | new_instance.stubs(:unknown) 100 | 101 | assert_fails(/unstubbed, expected exactly once/) { assert_matcher_accepts have_received(:unknown), new_instance } 102 | end 103 | 104 | def test_should_reject_not_enough_calls 105 | instance = new_instance 106 | instance.stubs(:magic) 107 | instance.magic 108 | message = /expected exactly twice/ 109 | assert_fails(message) { assert_received(instance, :magic) {|expect| expect.twice } } 110 | assert_fails(message) { assert_matcher_accepts have_received(:magic).twice, instance } 111 | end 112 | 113 | def test_should_reject_too_many_calls 114 | instance = new_instance 115 | instance.stubs(:magic) 116 | 2.times { instance.magic } 117 | message = /expected exactly once/ 118 | assert_fails(message) { assert_received(instance, :magic) {|expect| expect.once } } 119 | assert_fails(message) { assert_matcher_accepts have_received(:magic).once, instance } 120 | end 121 | 122 | def assert_fails(message=/not yet invoked/) 123 | begin 124 | yield 125 | rescue FailedAssertion => exception 126 | assert_match message, exception.message, "Test failed, but with the wrong message" 127 | return 128 | end 129 | flunk("Expected to fail") 130 | end 131 | 132 | end 133 | 134 | class PartialSpyTest < Test::Unit::TestCase 135 | include AcceptanceTest 136 | include SpyTestMethods 137 | 138 | def new_instance 139 | Object.new 140 | end 141 | end 142 | 143 | class PureSpyTest < Test::Unit::TestCase 144 | include AcceptanceTest 145 | include SpyTestMethods 146 | 147 | def new_instance 148 | stub 149 | end 150 | end 151 | 152 | class StubEverythingSpyTest < Test::Unit::TestCase 153 | include AcceptanceTest 154 | def setup 155 | setup_acceptance_test 156 | end 157 | 158 | def teardown 159 | teardown_acceptance_test 160 | end 161 | def test_should_match_invocations_with_no_explicit_stubbing 162 | instance = stub_everything 163 | instance.surprise! 164 | assert_received(instance, :surprise!) 165 | end 166 | end 167 | -------------------------------------------------------------------------------- /test/unit/mockery_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../test_helper', __FILE__) 2 | require 'bourne/mockery' 3 | require 'mocha/state_machine' 4 | require 'bourne/invocation' 5 | 6 | class MockeryTest < Test::Unit::TestCase 7 | 8 | include Mocha 9 | 10 | def test_should_build_instance_of_mockery 11 | mockery = Mockery.instance 12 | assert_not_nil mockery 13 | assert_kind_of Mockery, mockery 14 | end 15 | 16 | def test_should_cache_instance_of_mockery 17 | mockery_1 = Mockery.instance 18 | mockery_2 = Mockery.instance 19 | assert_same mockery_1, mockery_2 20 | end 21 | 22 | def test_should_expire_mockery_instance_cache 23 | mockery_1 = Mockery.instance 24 | Mockery.reset_instance 25 | mockery_2 = Mockery.instance 26 | assert_not_same mockery_1, mockery_2 27 | end 28 | 29 | def test_should_raise_expectation_error_because_not_all_expectations_are_satisfied 30 | mockery = Mockery.new 31 | mock_1 = mockery.named_mock('mock-1') { expects(:method_1) } 32 | mock_2 = mockery.named_mock('mock-2') { expects(:method_2) } 33 | 1.times { mock_1.method_1 } 34 | 0.times { mock_2.method_2 } 35 | assert_raises(ExpectationErrorFactory.exception_class) { mockery.verify } 36 | end 37 | 38 | def test_should_reset_list_of_mocks_on_teardown 39 | mockery = Mockery.new 40 | mock = mockery.unnamed_mock { expects(:my_method) } 41 | mockery.teardown 42 | assert_nothing_raised(ExpectationErrorFactory.exception_class) { mockery.verify } 43 | end 44 | 45 | def test_should_build_instance_of_stubba_on_instantiation 46 | mockery = Mockery.new 47 | assert_not_nil mockery.stubba 48 | assert_kind_of Central, mockery.stubba 49 | end 50 | 51 | def test_should_build_new_instance_of_stubba_on_teardown 52 | mockery = Mockery.new 53 | stubba_1 = mockery.stubba 54 | mockery.teardown 55 | stubba_2 = mockery.stubba 56 | assert_not_same stubba_1, stubba_2 57 | end 58 | 59 | def test_should_build_and_store_new_state_machine 60 | mockery = Mockery.new 61 | mockery.new_state_machine('state-machine-name') 62 | assert_equal 1, mockery.state_machines.length 63 | assert_kind_of StateMachine, mockery.state_machines[0] 64 | end 65 | 66 | def test_should_reset_list_of_state_machines_on_teardown 67 | mockery = Mockery.new 68 | mockery.new_state_machine('state-machine-name') 69 | mockery.teardown 70 | assert_equal 0, mockery.state_machines.length 71 | end 72 | 73 | class FakeMethod 74 | def stub; end 75 | def unstub; end 76 | def matches?(other); true; end 77 | end 78 | 79 | def test_should_unstub_all_methods_on_teardown 80 | mockery = Mockery.new 81 | stubba = mockery.stubba 82 | stubba.stub(FakeMethod.new) 83 | mockery.teardown 84 | assert stubba.stubba_methods.empty? 85 | end 86 | 87 | def test_should_display_object_id_for_mocha_inspect_if_mock_has_no_name 88 | mockery = Mockery.new 89 | mock = mockery.unnamed_mock 90 | assert_match Regexp.new("^#$"), mock.mocha_inspect 91 | end 92 | 93 | def test_should_display_object_id_for_inspect_if_mock_has_no_name 94 | mockery = Mockery.new 95 | mock = mockery.unnamed_mock 96 | assert_match Regexp.new("^#$"), mock.inspect 97 | end 98 | 99 | def test_should_display_name_for_mocha_inspect_if_mock_has_string_name 100 | mockery = Mockery.new 101 | mock = mockery.named_mock('named_mock') 102 | assert_equal "#", mock.mocha_inspect 103 | end 104 | 105 | def test_should_display_name_for_mocha_inspect_if_mock_has_symbol_name 106 | mockery = Mockery.new 107 | mock = mockery.named_mock(:named_mock) 108 | assert_equal "#", mock.mocha_inspect 109 | end 110 | 111 | def test_should_display_name_for_inspect_if_mock_has_string_name 112 | mockery = Mockery.new 113 | mock = mockery.named_mock('named_mock') 114 | assert_equal "#", mock.inspect 115 | end 116 | 117 | def test_should_display_name_for_inspect_if_mock_has_symbol_name 118 | mockery = Mockery.new 119 | mock = mockery.named_mock(:named_mock) 120 | assert_equal "#", mock.inspect 121 | end 122 | 123 | def test_should_display_impersonated_object_for_mocha_inspect 124 | mockery = Mockery.new 125 | instance = Object.new 126 | mock = mockery.mock_impersonating(instance) 127 | assert_equal "#{instance.mocha_inspect}", mock.mocha_inspect 128 | end 129 | 130 | def test_should_display_impersonated_object_for_inspect 131 | mockery = Mockery.new 132 | instance = Object.new 133 | mock = mockery.mock_impersonating(instance) 134 | assert_equal "#{instance.mocha_inspect}", mock.inspect 135 | end 136 | 137 | class FakeClass; end 138 | 139 | def test_should_display_any_instance_prefix_followed_by_class_whose_instances_are_being_impersonated_for_mocha_inspect 140 | mockery = Mockery.new 141 | mock = mockery.mock_impersonating_any_instance_of(FakeClass) 142 | assert_equal "#", mock.mocha_inspect 143 | end 144 | 145 | def test_should_display_any_instance_prefix_followed_by_class_whose_instances_are_being_impersonated_for_inspect 146 | mockery = Mockery.new 147 | mock = mockery.mock_impersonating_any_instance_of(FakeClass) 148 | assert_equal "#", mock.inspect 149 | end 150 | 151 | def test_should_record_invocation 152 | mock = 'a mock' 153 | method = :call_me 154 | args = [1, 2] 155 | mockery = Mockery.new 156 | mockery.invocation(mock, method, args) 157 | assert_equal 1, mockery.invocations.size 158 | invocation = mockery.invocations.first 159 | assert_equal mock, invocation.mock 160 | assert_equal method, invocation.method_name 161 | assert_equal args, invocation.arguments 162 | end 163 | 164 | end 165 | -------------------------------------------------------------------------------- /test/unit/mock_test.rb: -------------------------------------------------------------------------------- 1 | # to update: 2 | # - copy new code from https://raw.github.com/freerange/mocha/master/test/unit/mock_test.rb 3 | # - keep: require 'bourne/mock' 4 | # - keep: FakeExpectation test 5 | require File.expand_path('../../test_helper', __FILE__) 6 | require 'bourne/mock' 7 | require 'mocha/expectation_error_factory' 8 | require 'set' 9 | require 'simple_counter' 10 | 11 | class MockTest < Test::Unit::TestCase 12 | 13 | include Mocha 14 | 15 | def test_should_set_single_expectation 16 | mock = build_mock 17 | mock.expects(:method1).returns(1) 18 | assert_nothing_raised(ExpectationErrorFactory.exception_class) do 19 | assert_equal 1, mock.method1 20 | end 21 | end 22 | 23 | def test_should_build_and_store_expectations 24 | mock = build_mock 25 | expectation = mock.expects(:method1) 26 | assert_not_nil expectation 27 | assert_equal [expectation], mock.__expectations__.to_a 28 | end 29 | 30 | def test_should_not_stub_everything_by_default 31 | mock = build_mock 32 | assert_equal false, mock.everything_stubbed 33 | end 34 | 35 | def test_should_stub_everything 36 | mock = build_mock 37 | mock.stub_everything 38 | assert_equal true, mock.everything_stubbed 39 | end 40 | 41 | def test_should_be_able_to_extend_mock_object_with_module 42 | mock = build_mock 43 | assert_nothing_raised(ExpectationErrorFactory.exception_class) { mock.extend(Module.new) } 44 | end 45 | 46 | def test_should_be_equal 47 | mock = build_mock 48 | assert_equal true, mock.eql?(mock) 49 | end 50 | 51 | if RUBY_VERSION < '1.9' 52 | OBJECT_METHODS = STANDARD_OBJECT_PUBLIC_INSTANCE_METHODS.reject { |m| m =~ /^__.*__$/ || ["method_missing", "singleton_method_undefined", "initialize"].include?(m)} 53 | else 54 | OBJECT_METHODS = STANDARD_OBJECT_PUBLIC_INSTANCE_METHODS.reject { |m| m =~ /^__.*__$/ || [:object_id, :method_missing, :singleton_method_undefined, :initialize, :String, :singleton_method_added].include?(m) } 55 | end 56 | 57 | def test_should_be_able_to_mock_standard_object_methods 58 | mock = build_mock 59 | OBJECT_METHODS.each { |method| mock.__expects__(method.to_sym).returns(method) } 60 | OBJECT_METHODS.each { |method| assert_equal method, mock.__send__(method.to_sym) } 61 | assert mock.__verified__? 62 | end 63 | 64 | def test_should_be_able_to_stub_standard_object_methods 65 | mock = build_mock 66 | OBJECT_METHODS.each { |method| mock.__stubs__(method.to_sym).returns(method) } 67 | OBJECT_METHODS.each { |method| assert_equal method, mock.__send__(method.to_sym) } 68 | end 69 | 70 | def test_should_create_and_add_expectations 71 | mock = build_mock 72 | expectation1 = mock.expects(:method1) 73 | expectation2 = mock.expects(:method2) 74 | assert_equal [expectation1, expectation2].to_set, mock.__expectations__.to_set 75 | end 76 | 77 | def test_should_pass_backtrace_into_expectation 78 | mock = build_mock 79 | backtrace = Object.new 80 | expectation = mock.expects(:method1, backtrace) 81 | assert_equal backtrace, expectation.backtrace 82 | end 83 | 84 | def test_should_pass_backtrace_into_stub 85 | mock = build_mock 86 | backtrace = Object.new 87 | stub = mock.stubs(:method1, backtrace) 88 | assert_equal backtrace, stub.backtrace 89 | end 90 | 91 | def test_should_create_and_add_stubs 92 | mock = build_mock 93 | stub1 = mock.stubs(:method1) 94 | stub2 = mock.stubs(:method2) 95 | assert_equal [stub1, stub2].to_set, mock.__expectations__.to_set 96 | end 97 | 98 | def test_should_invoke_expectation_and_return_result 99 | mock = build_mock 100 | mock.expects(:my_method).returns(:result) 101 | result = mock.my_method 102 | assert_equal :result, result 103 | end 104 | 105 | def test_should_not_raise_error_if_stubbing_everything 106 | mock = build_mock 107 | mock.stub_everything 108 | result = nil 109 | assert_nothing_raised(ExpectationErrorFactory.exception_class) do 110 | result = mock.unexpected_method 111 | end 112 | assert_nil result 113 | end 114 | 115 | def test_should_raise_assertion_error_for_unexpected_method_call 116 | mock = build_mock 117 | error = assert_raise(ExpectationErrorFactory.exception_class) do 118 | mock.unexpected_method_called(:my_method, :argument1, :argument2) 119 | end 120 | assert_match(/unexpected invocation/, error.message) 121 | assert_match(/my_method/, error.message) 122 | assert_match(/argument1/, error.message) 123 | assert_match(/argument2/, error.message) 124 | end 125 | 126 | def test_should_not_verify_successfully_because_not_all_expectations_have_been_satisfied 127 | mock = build_mock 128 | mock.expects(:method1) 129 | mock.expects(:method2) 130 | mock.method1 131 | assert !mock.__verified__? 132 | end 133 | 134 | def test_should_increment_assertion_counter_for_every_verified_expectation 135 | mock = build_mock 136 | 137 | mock.expects(:method1) 138 | mock.method1 139 | 140 | mock.expects(:method2) 141 | mock.method2 142 | 143 | assertion_counter = SimpleCounter.new 144 | 145 | mock.__verified__?(assertion_counter) 146 | 147 | assert_equal 2, assertion_counter.count 148 | end 149 | 150 | def test_should_yield_supplied_parameters_to_block 151 | mock = build_mock 152 | parameters_for_yield = [1, 2, 3] 153 | mock.expects(:method1).yields(*parameters_for_yield) 154 | yielded_parameters = nil 155 | mock.method1() { |*parameters| yielded_parameters = parameters } 156 | assert_equal parameters_for_yield, yielded_parameters 157 | end 158 | 159 | def test_should_set_up_multiple_expectations_with_return_values 160 | mock = build_mock 161 | mock.expects(:method1 => :result1, :method2 => :result2) 162 | assert_equal :result1, mock.method1 163 | assert_equal :result2, mock.method2 164 | end 165 | 166 | def test_should_set_up_multiple_stubs_with_return_values 167 | mock = build_mock 168 | mock.stubs(:method1 => :result1, :method2 => :result2) 169 | assert_equal :result1, mock.method1 170 | assert_equal :result2, mock.method2 171 | end 172 | 173 | def test_should_keep_returning_specified_value_for_stubs 174 | mock = build_mock 175 | mock.stubs(:method1).returns(1) 176 | assert_equal 1, mock.method1 177 | assert_equal 1, mock.method1 178 | end 179 | 180 | def test_should_keep_returning_specified_value_for_expects 181 | mock = build_mock 182 | mock.expects(:method1).times(2).returns(1) 183 | assert_equal 1, mock.method1 184 | assert_equal 1, mock.method1 185 | end 186 | 187 | def test_should_match_most_recent_call_to_expects 188 | mock = build_mock 189 | mock.expects(:method1).returns(0) 190 | mock.expects(:method1).returns(1) 191 | assert_equal 1, mock.method1 192 | end 193 | 194 | def test_should_match_most_recent_call_to_stubs 195 | mock = build_mock 196 | mock.stubs(:method1).returns(0) 197 | mock.stubs(:method1).returns(1) 198 | assert_equal 1, mock.method1 199 | end 200 | 201 | def test_should_match_most_recent_call_to_stubs_or_expects 202 | mock = build_mock 203 | mock.stubs(:method1).returns(0) 204 | mock.expects(:method1).returns(1) 205 | assert_equal 1, mock.method1 206 | end 207 | 208 | def test_should_match_most_recent_call_to_expects_or_stubs 209 | mock = build_mock 210 | mock.expects(:method1).returns(0) 211 | mock.stubs(:method1).returns(1) 212 | assert_equal 1, mock.method1 213 | end 214 | 215 | def test_should_respond_to_expected_method 216 | mock = build_mock 217 | mock.expects(:method1) 218 | assert_equal true, mock.respond_to?(:method1) 219 | end 220 | 221 | def test_should_respond_to_expected_method_as_string 222 | mock = build_mock 223 | mock.expects(:method1) 224 | assert_equal true, mock.respond_to?('method1') 225 | end 226 | 227 | def test_should_not_respond_to_unexpected_method 228 | mock = build_mock 229 | assert_equal false, mock.respond_to?(:method1) 230 | end 231 | 232 | def test_should_respond_to_methods_which_the_responder_does_responds_to 233 | instance = Class.new do 234 | define_method(:respond_to?) { |symbol| true } 235 | end.new 236 | mock = build_mock 237 | mock.responds_like(instance) 238 | assert_equal true, mock.respond_to?(:invoked_method) 239 | end 240 | 241 | def test_should_not_respond_to_methods_which_the_responder_does_not_responds_to 242 | instance = Class.new do 243 | define_method(:respond_to?) { |symbol| false } 244 | end.new 245 | mock = build_mock 246 | mock.responds_like(instance) 247 | assert_equal false, mock.respond_to?(:invoked_method) 248 | end 249 | 250 | def test_should_respond_to_methods_which_the_responder_instance_does_responds_to 251 | klass = Class.new do 252 | define_method(:respond_to?) { |symbol| true } 253 | end 254 | mock = build_mock 255 | mock.responds_like_instance_of(klass) 256 | assert_equal true, mock.respond_to?(:invoked_method) 257 | end 258 | 259 | def test_should_not_respond_to_methods_which_the_responder_instance_does_not_responds_to 260 | klass = Class.new do 261 | define_method(:respond_to?) { |symbol| false } 262 | end 263 | mock = build_mock 264 | mock.responds_like_instance_of(klass) 265 | assert_equal false, mock.respond_to?(:invoked_method) 266 | end 267 | 268 | def test_respond_like_should_return_itself_to_allow_method_chaining 269 | mock = build_mock 270 | assert_same mock.responds_like(Object.new), mock 271 | end 272 | 273 | def test_respond_like_instance_of_should_return_itself_to_allow_method_chaining 274 | mock = build_mock 275 | assert_same mock.responds_like_instance_of(Object), mock 276 | end 277 | 278 | def test_should_not_raise_no_method_error_if_mock_is_not_restricted_to_respond_like_a_responder 279 | mock = build_mock 280 | mock.stubs(:invoked_method) 281 | assert_nothing_raised(NoMethodError) { mock.invoked_method } 282 | end 283 | 284 | def test_should_not_raise_no_method_error_if_responder_does_respond_to_invoked_method 285 | instance = Class.new do 286 | define_method(:respond_to?) { |symbol| true } 287 | end.new 288 | mock = build_mock 289 | mock.responds_like(instance) 290 | mock.stubs(:invoked_method) 291 | assert_nothing_raised(NoMethodError) { mock.invoked_method } 292 | end 293 | 294 | def test_should_raise_no_method_error_if_responder_does_not_respond_to_invoked_method 295 | instance = Class.new do 296 | define_method(:respond_to?) { |symbol| false } 297 | define_method(:mocha_inspect) { 'mocha_inspect' } 298 | end.new 299 | mock = build_mock 300 | mock.responds_like(instance) 301 | mock.stubs(:invoked_method) 302 | assert_raises(NoMethodError) { mock.invoked_method } 303 | end 304 | 305 | def test_should_raise_no_method_error_with_message_indicating_that_mock_is_constrained_to_respond_like_responder 306 | instance = Class.new do 307 | define_method(:respond_to?) { |symbol| false } 308 | define_method(:mocha_inspect) { 'mocha_inspect' } 309 | end.new 310 | mock = build_mock 311 | mock.responds_like(instance) 312 | mock.stubs(:invoked_method) 313 | begin 314 | mock.invoked_method 315 | rescue NoMethodError => e 316 | assert_match(/which responds like mocha_inspect/, e.message) 317 | end 318 | end 319 | 320 | def test_should_handle_respond_to_with_private_methods_param_without_error 321 | mock = build_mock 322 | assert_nothing_raised { mock.respond_to?(:object_id, false) } 323 | end 324 | 325 | def test_should_respond_to_any_method_if_stubbing_everything 326 | mock = build_mock 327 | mock.stub_everything 328 | assert mock.respond_to?(:abc) 329 | assert mock.respond_to?(:xyz) 330 | end 331 | 332 | def test_should_remove_expectation_for_unstubbed_method 333 | mock = build_mock 334 | mock.expects(:method1) 335 | mock.unstub(:method1) 336 | e = assert_raises(ExpectationErrorFactory.exception_class) { mock.method1 } 337 | assert_match(/unexpected invocation/, e.message) 338 | end 339 | 340 | class FakeExpectation 341 | attr_reader :args 342 | 343 | def invoke 344 | end 345 | 346 | def match?(*args) 347 | true 348 | end 349 | 350 | def invocations_allowed? 351 | true 352 | end 353 | end 354 | 355 | def test_should_record_invocations 356 | method = :a_method 357 | args = [1, 2] 358 | mock = Mock.new(method) 359 | expectation = FakeExpectation.new 360 | mock.__expectations__.add expectation 361 | mock.send(method, *args) 362 | end 363 | 364 | private 365 | 366 | def build_mock 367 | Mock.new(nil) 368 | end 369 | end 370 | --------------------------------------------------------------------------------