Skip to content

Commit

Permalink
+ Added Object#stub (in minitest/mock.rb).
Browse files Browse the repository at this point in the history
[git-p4: depot-paths = "//src/minitest/dev/": change = 7432]
  • Loading branch information
zenspider committed May 9, 2012
1 parent 6184d68 commit c249eda
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 3 deletions.
1 change: 1 addition & 0 deletions .autotest
Expand Up @@ -10,6 +10,7 @@ Autotest.add_hook :initialize do |at|
at.extra_class_map["TestMeta"] = "test/test_minitest_spec.rb"
at.extra_class_map["TestMiniTestUnitTestCase"] = "test/test_minitest_unit.rb"
at.extra_class_map["TestMiniTestUnit"] = "test/test_minitest_unit.rb"
at.extra_class_map["TestMiniTestStub"] = "test/test_minitest_mock.rb"
at.add_exception 'coverage.info'
at.add_exception 'coverage'
end
Expand Down
18 changes: 15 additions & 3 deletions README.txt
Expand Up @@ -33,8 +33,8 @@ algorithms in a repeatable manner. Now you can assert that your newb
co-worker doesn't replace your linear algorithm with an exponential
one!

minitest/mock by Steven Baker, is a beautifully tiny mock object
framework.
minitest/mock by Steven Baker, is a beautifully tiny mock (and stub)
object framework.

minitest/pride shows pride in testing and adds coloring to your test
output. I guess it is an example of how to write IO pipes too. :P
Expand All @@ -54,7 +54,7 @@ discovery.
* minitest/autorun - the easy and explicit way to run all your tests.
* minitest/unit - a very fast, simple, and clean test system.
* minitest/spec - a very fast, simple, and clean spec system.
* minitest/mock - a simple and clean mock system.
* minitest/mock - a simple and clean mock/stub system.
* minitest/benchmark - an awesome way to assert your algorithm's performance.
* minitest/pride - show your pride in testing!
* Incredibly small and fast runner, but no bells and whistles.
Expand Down Expand Up @@ -194,6 +194,18 @@ Output is tab-delimited to make it easy to paste into a spreadsheet.
end
end

=== Stubs

def test_stale_eh
obj_under_test = Something.new

refute obj_under_test.stale?

Time.stub :now, Time.at(0) do # stub goes away once the block is done
assert obj_under_test.stale?
end
end

=== Customizable Test Runner Types:

MiniTest::Unit.runner=(runner) provides an easy way of creating custom
Expand Down
38 changes: 38 additions & 0 deletions lib/minitest/mock.rb
Expand Up @@ -125,3 +125,41 @@ def respond_to?(sym) # :nodoc:
end
end
end

class Object # :nodoc:

##
# Add a temporary stubbed method replacing +name+ for the duration
# of the +block+. If +val_or_callable+ responds to #call, then it
# returns the result of calling it, otherwise returns the value
# as-is. Cleans up the stub at the end of the +block+.
#
# def test_stale_eh
# obj_under_test = Something.new
# refute obj_under_test.stale?
#
# Time.stub :now, Time.at(0) do
# assert obj_under_test.stale?
# end
# end

def stub name, val_or_callable, &block
new_name = "__minitest_stub__#{name}"

metaclass = class << self; self; end
metaclass.send :alias_method, new_name, name
metaclass.send :define_method, name do |*args|
if val_or_callable.respond_to? :call then
val_or_callable.call(*args)
else
val_or_callable
end
end

yield
ensure
metaclass.send :undef_method, name

This comment has been minimized.

Copy link
@frodsan

frodsan Oct 1, 2013

Hi @zenspider, I was reviewing how Minitest implements stubs. I just want to know, why it undefs the method if then will be overwritten by the alias? Maybe I'm missing something. Anyway, I would be very grateful if you could answer this. thanks!

This comment has been minimized.

Copy link
@phiggins

phiggins Oct 1, 2013

Ruby will emit a warning if you redefine a method without first undefining it. Aliasing counts as method definition as far as warnings go.

$ cat foo_bar.rb
class FooBar
  def foo ; :foo ; end
  def bar ; :bar ; end

  alias_method :foo, :bar
end
$ ruby -w foo_bar.rb
foo_bar.rb:5: warning: method redefined; discarding old foo
foo_bar.rb:2: warning: previous definition of foo was here

This comment has been minimized.

Copy link
@frodsan

frodsan Oct 1, 2013

thanks!

This comment has been minimized.

Copy link
@tonytonyjan

tonytonyjan Sep 23, 2021

I wonder why we use undef_method instead of remove_method here? I think remove_method makes more sense because it does less thing than undef_method and it is the real opposite operation of define_method.

This comment has been minimized.

Copy link
@tonytonyjan

tonytonyjan Sep 23, 2021

Performance's perspective for undef_method vs remove_method

                    user     system      total        real
remove_method   0.632000   0.000000   0.632000 (  0.633532)
undef_method    0.860000   0.008000   0.868000 (  0.871581)
benchmark
require 'benchmark'

A = Class.new

Benchmark.bmbm do |x|
  n = 1_000_000
  empty_lambda = -> {}
  x.report('remove_method') do
    n.times do
      A.define_method :foo, &empty_lambda
      A.remove_method :foo
    end
  end

  x.report('undef_method') do
    n.times do
      A.define_method :foo, &empty_lambda
      A.undef_method :foo
    end
  end
end

This comment has been minimized.

Copy link
@zenspider

zenspider Dec 15, 2021

Author Collaborator

Commenting on an 8 year old commit… maybe not the best place.

This comment has been minimized.

Copy link
@tonytonyjan

tonytonyjan Dec 15, 2021

@zenspider Wonder what is the best place in your opnion? How about we creating a PR that brings the discussion up?

metaclass.send :alias_method, name, new_name
metaclass.send :undef_method, new_name
end
end
59 changes: 59 additions & 0 deletions test/minitest/test_minitest_mock.rb
Expand Up @@ -203,3 +203,62 @@ def util_verify_bad exp
assert_equal exp, e.message
end
end

require "test/minitest/metametameta"

class TestMiniTestStub < MiniTest::Unit::TestCase
def setup
super
MiniTest::Unit::TestCase.reset

@tc = MiniTest::Unit::TestCase.new 'fake tc'
@assertion_count = 1
end

def teardown
super
assert_equal @assertion_count, @tc._assertions
end

def assert_stub val_or_callable
@assertion_count += 1

t = Time.now.to_i

Time.stub :now, val_or_callable do
@tc.assert_equal 42, Time.now
end

@tc.assert_operator Time.now.to_i, :>=, t
end

def test_stub_value
assert_stub 42
end

def test_stub_block
assert_stub lambda { 42 }
end

def test_stub_block_args
@assertion_count += 1

t = Time.now.to_i

Time.stub :now, lambda { |n| n * 2 } do
@tc.assert_equal 42, Time.now(21)
end

@tc.assert_operator Time.now.to_i, :>=, t
end

def test_stub_callable
obj = Object.new

def obj.call
42
end

assert_stub obj
end
end

0 comments on commit c249eda

Please sign in to comment.