在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 "\n#{@tests.keys.size} tests, #{@success_count} success, #{@failure_count} failure" end |
我们的executor代码非常简单。输出”describe” block的描述,然后遍历所有存储的”it” block并且在executor对象中执行它们。这么处理没有什么特别原因,但这意味着executor对象同样也可以包含其他函数,并且可以在”it” block中作为一种“语言”来使用(比如,我们dsl的一部分可以定义为executor的一个函数)。譬如,我们可以在executor上定义下列函数:
|
这个函数同样可以在”it” block内部使用,但对于我们这个简单的测试没有这个必要。
所以,”it” block会计算并存储结果,通常结果只是”it” block最后一个语句的返回值(按照常规的Ruby)。这里,我们希望确保最后一个语句总是返回一个布尔值(标明测试通过或失败),通过它我们可以输出一些有意义提示。
我们还差最后一步,”should”函数代码如下:
|
每个对象都应当提供自己”should”函数,代码如下:
|
这个函数并没有真正做什么工作(仅仅是返回对象本身);它仅仅是一个让测试读起来更好的语法。