注:本文以 minitest 的最新master 分支 baf6010 ,版本为5.8.4为基础。 所有代码可以在https://github.com/minitest_source[minitest_source] 找到.

一个简单的测试

# 代码 1.1
# dog.rb
class Dog
  def spark
    'Spark!'
  end
end

# dog_test.rb
require 'minitest/autorun'
require_relative './dog'

class DogTest < Minitest::Test
  def setup
    @dog = Dog.new
  end

  def test_dog_should_spark
    assert_respond_to @dog, :spark
    assert_equal 'Spark!', @dog.spark
  end

  def ordiary_method
    assert true
  end
end

如何执行测试呢?

$ ruby dog_test.rb
Run options: --seed 24057

# Running:
.

Finished in 0.001039s, 962.5723 runs/s, 1925.1446 assertions/s.

1 runs, 2 assertions, 0 failures, 0 errors, 0 skips

如果我们把require 'minitest/autorun' 这一行注释掉,然后再执行ruby dog_test.rb,程序是否还 正常执行呢?让我们执行下:

$ ruby dog_test.rb

这一次什么都没有输出。Why?

这一行有什么魔法呢?为什么有了它之后,可以执行测试代码, 还可以输出测试结果?

那我们看一下Minitest 的代码,就可以了然了。

如果列位有兴趣,可以继续往下看。

Minitest 的工作原理

Minitest 主要代码的结构

minitest
│   ├── assertions.rb   # 定义 assert_*方法
│   ├── autorun.rb      # 自动执行测试
│   ├── benchmark.rb    # Benchmark 相关方法
│   ├── expectations.rb # 使用`must` 代替`assert`
│   ├── hell.rb         # 并行执行测试
│   ├── mock.rb         # Mock 的 expect 相关实现
│   ├── parallel.rb     # 多线程执行测试
│   ├── pride.rb        # Report 的一种,以颜色展示结果
│   ├── pride_plugin.rb # Pride plugin 的具体实现
│   ├── spec.rb         # spec 实现,本质上是一种语法糖
│   ├── test.rb         # Minitest 具体执行部分
│   └── unit.rb         # test/unit 的 Minitest 实现
└── minitest.rb         # Minitest 的抽象层实现

Minitest 的代码行数统计数据:

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Ruby                            13            646           1061           1593
-------------------------------------------------------------------------------
SUM:                            13            646           1061           1593
-------------------------------------------------------------------------------

以上是使用http://cloc.sourceforge.net/[cloc] 统计得出。

Minitest 用了不到1600行的代码,集扩展性强、兼容性好、可读性强于一身。

从上面的代码,我们可以得到以下结论:

  • 测试文件需要加载minitest/autorun 文件
  • 测试类要继承自Minitest::Test 或者其子类
  • 测试方法需要以test_ 开头。(可以看到上面只有2个 assert 执行了,里面其实有3个 assert 语句)
  • 测试结束会输出执行的结果(是否通过、失败、跳过以及执行时间和速度)
  • Minitest 有钩子的存在(比如setup)

那么我们会有以下疑问:

  • minitest/autorun 到底做了什么?
  • 继承Minitest::Test的目的何在,它内部有什么特殊方法?
  • 为什么以test_ 开头的方法执行了,而普通的方法没有执行?里面肯定有一个"惊天的阴谋"
  • Minitest 的结果是何时,如何打出来的?
  • Minitest有哪些钩子,调用顺序几何?

以上问题,我们在下一节讨论。

参考文献: