在describe block中,”it”函数会为每个”it” block调用一次。每次调用时,会把输入的block以测试描述作为键值存储在哈希表中。完成这些以后,我们只要创建一个Executor对象,可以对我们所有的测试block进行迭代,调用它们并产生执行结果。Executor代码如下:

class Executor
  def initialize(description, tests)
    @description = description
    @tests = tests
    @success_count = 0
    @failure_count = 0
  end
  def execute
    puts "#{@description}"
    @tests.each_pair do |name, block|
      print " - #{name}"
      result = self.instance_eval(&block)
      result ? @success_count += 1 : @failure_count += 1
      puts result ? " SUCCESS" : " FAILURE"
    end
    summary
  end
  def summary
    puts " #{@tests.keys.size} tests, #{@success_count} success, #{@failure_count} failure"
  end


  我们的executor代码非常简单。输出”describe” block的描述,然后遍历所有存储的”it” block并且在executor对象中执行它们。这么处理没有什么特别原因,但这意味着executor对象同样也可以包含其他函数,并且可以在”it” block中作为一种“语言”来使用(比如,我们dsl的一部分可以定义为executor的一个函数)。譬如,我们可以在executor上定义下列函数:

def should_be_five(x)
  5 == x
end
 


  这个函数同样可以在”it” block内部使用,但对于我们这个简单的测试没有这个必要。

  所以,”it” block会计算并存储结果,通常结果只是”it” block后一个语句的返回值(按照常规的Ruby)。这里,我们希望确保后一个语句总是返回一个布尔值(标明测试通过或失败),通过它我们可以输出一些有意义提示。

  我们还差后一步,”should”函数代码如下:

true.should == true
5.should == 5
 


  每个对象都应当提供自己”should”函数,代码如下:

class Object
  def should
    self
  end
end
 


  这个函数并没有真正做什么工作(仅仅是返回对象本身);它仅仅是一个让测试读起来更好的语法。