这里要做的是在Dsl对象的上下文里对block求值:

1 self.instance_eval(&block)

  我们的Dsl对象有一个”it”函数,同样也接收一个描述和一个block,这里和describe block包含的内容完全一致,一切都运行得很好(例如,我们基本上会在几个函数调用时使用”it”函数,每次都传入一个描述和一个block)。我们还可以在Dsl对象中定义其他的函数,并且这些函数会成为允许在”describe” block中使用的“语言”的一部分)。

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

1  class Executor
2  def initialize(description, tests)
3   @description = description
4   @tests = tests
5    @success_count = 0
6     @failure_count = 0
7    end
8    def execute
9     puts "#{@description}"
10     @tests.each_pair do |name, block|
11     print " - #{name}"
12      result = self.instance_eval(&block)
13    result ? @success_count += 1 : @failure_count += 1
14    puts result ? " SUCCESS" : " FAILURE"
15    end
16    summary
17     end
18    def summary
19   puts " #{@tests.keys.size} tests, #{@success_count} success, #{@failure_count} failure"
20   end
21   end

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

1  def should_be_five(x)
2  5 == x
3    end 
  这个函数同样可以在”it” block内部使用,但对于我们这个简单的测试没有这个必要。

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

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