30 September 2013

Flexible Mock

Hey, I've been posting too many abstract shit lately, how about some code?

So here's my simple Mock class and tests for it (to show how it works).

Class


class Mock
  def initialize(method_name, block = nil )
    block||=Proc.new do |*args|
      args=args.first if args.length==1
      yield(args)
    end
    self.class.send :define_method, method_name, block
  end
end

Examples


class TestMock < MiniTest::Unit::TestCase
  def test_mock
    fake = Mock.new :some_method do
      'value'
    end
    assert fake.some_method == 'value'
  end

  def test_mock_single_parameter
    fake = Mock.new :some_method do |p|
      p*2
    end
    assert_equal fake.some_method(2),  4
  end

  def test_mock_many_parameters
    fake = Mock.new :some_method do |p1, p2, p3|
      p1*p2*p3
    end
    assert_equal fake.some_method(2, 3, 4), 24
  end

  def test_lambda_parameter
    fake = Mock.new :some_method, lambda {|p| p*2}
    assert_equal fake.some_method(2), 4
  end

  def test_access_to_local_variable
    p1=1
    fake = Mock.new :some_method do
      p1+=1
    end
    fake.some_method
    assert_equal p1, 2
  end
end

The problem with other mock libraries is that there's too much magic going on and syntax is tricky

    mock.expect :method, 'return', [:params]
    ...
    mock.verify
    # or
    mock.should_receive(:method).with("A", 1, 3).once
In more complex examples it gets hard to express what you want and remember syntax. I prefer more control, that's why my decision is to create object with custom method. In this method you can do whatever you want.

Another good thing of this approach is property of Proc to maintain context where it was called (look at the last example). This eliminates any need for internal verification via "mock.verify" since you can do it outside.

p.s. syntax highlighting I used can be found at github: highlight.js

No comments:

Post a Comment