遭遇mocha中的两个小陷阱

  mocha是ruby下的一个mock框架。

  关于mock object的相关信息,请参考:http://www.mockobjects.com/

  关于mocha的相关信息,请参考:http://mocha.rubyforge.org/

  基本概念及使用方法这里不谈,就谈谈最近在mocha碰到的两个小陷阱。

 

  1.情况是这么个情况,完成了测试代码与功能代码后,我开始着手进行测试代码的重构(需要吗?不需要吗?)。首先,我盯上了刚完成测试代码时,就看着不爽的一段代码,这里整理如下:

def generate_some_mock_object(return1,return2)
  mo=mock()
  mo.stubs(:mock_method1).returns(return1)
  mo.stubs(:mock_method2).returns(return2)
  mo
end

   这是我测试代码里的一个helper方法,用于生成一个mock对象,并stub了两个方法。味道是不怎么好,不是吗?于是,我决定重构她。扫了眼mocha的文档,发现mocha给出了一个mock对象生成的例子:

product = mock('ipod_product', :manufacturer => 'ipod', :price => 100)

   哇哈哈,一个hash就搞定,而且还不用定义一个额外的局部变量,happy,动手将我的helper方法改为如下形式:

def generate_some_mock_object(return1,return2)
  mock(:mock_method1 => return1,:mock_method2 => return2)
end

   我happy的重新跑了一把测试,一串“F”出现在我面前,提示了一些诸如“expected call 1, not call 0”之类的错误。我就纳闷了,不都是生成一个mock对象,mock了两个stud方法么?why?仔细查看mocha文档和源代码,才发现通过mock(...)方法生成mock对象的时候,如果传递了一串excepted方法的hash时,mocha内部不用通过stubs(...)方法,生成stub方法,而是通过expects(...)方法。而expects(...)生成的方法在测试的时候必须进行调用,才认为测试通过,否则测试被认为失败的。而我的功能代码正好是只调用了{:mock_method1,:mock_method2}中的一个,所以造成测试失败。下面是mock(...)方法的源代码:

def mock(*arguments, &block)
  name = arguments.shift if arguments.first.is_a?(String)
  expectations = arguments.shift || {}
  mock = name ? Mock.named(name, &block) : Mock.unnamed(&block)
  mock.expects(expectations)
  mocks << mock
  mock
end

问题就出在mock.expects(expectations) 这句代码上。
   难道没办法完成这次重构吗?当然不是,也怪我看文档不仔细,其实mocha为我们提供了另外一个生成mock对象的方法:stub(...) (注意:不要和stubs方法混淆),这里,使用stub方法,可以很轻松的完成我的重构,结果如下:

def generate_some_mock_object(return1,return2)
  stub(:mock_method1 => return1,:mock_method2 => return2)
end

   重新跑一遍测试,全是"...",那个happy啊。。。stub方法的源代码如下:

def stub(*arguments, &block)
  name = arguments.shift if arguments.first.is_a?(String)
  expectations = arguments.shift || {}
  stub = name ? Mock.named(name, &block) : Mock.unnamed(&block)
  stub.stubs(expectations)
  mocks << stub
  stub
end 

  发现关键所在的stub.stubs(expectations) 代码了吧?:)

   PS:我发现mocha内部也有臭的迹象,mock和stub~呵呵

 

   2.事情是这么个事情,后来,我盯上了这么一段测试代码,整理如下:

def generate_some_stub_method(return1,return2)
  @my_object.stubs(:method1).returns(return1)
  @my_object.stubs(:method2).returns(return2)
end

   这里,@my_object不是一个mock对象,而是我实在的一个测试用的业务对象,这个helper方法无非就是为该对象生成两个stub方法,以供测试用。我仍然看她不怎么爽,同样,mocha的文档给了一个例子如下:

object.stubs(:method1 => :result1, :method2 => :result2)

   继续happy,又可以合二为一了。于是,我将这个helper方法重构如下:

def generate_some_stub_method(return1,return2)
  @my_object.stubs(:method1 =>return1,:method2 => return2)
end

   我又happy着重新跑了一遍测试,居然提示我"{:method1 =>return1,:method2 => return2} is not a Symbol"。这个。。。老大,我承认,他绝对不是一个Symbol,但是你的示意代码和文档明明标明了stubs明明可以接受一个hash的方法集合啊?我有文档作证:

  stubs(method_name) → expectation
  stubs(method_names) → last expectation

  并且,我查看源代码,她明明可以处理hash的啊。bug?但是我又错了。。。后来又仔细查看了mocha的源代码,发现,在mocha/object.rb文件中,有如下定义:

class Object
  ......
  def stubs(symbol) 
    method = stubba_method.new(stubba_object, symbol)
    $stubba.stub(method)
    mocha.stubs(symbol, caller)
  end
  ......
end

   哦!问题出在这里!我前面说了,@my_object对象不是一个mock对象,而是一个普通的oject,我只是在这个普通的object上面搞了两个stub方法上去,自然,调用的是Object#stubs方法了,而不是Mock#stubs方法。原来如此~看来,这个重构完不成了,不过还好,不算太臭,就这么着贝~

  OK!继续接下来的测试吧~:)

阅读更多
文章标签: Ruby 框架 F#
个人分类: TDD
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭