Rspecのコードリーディング

bundle exec rspecを実行したときの処理を追ってみた。
まずは、binをみてみる。

vendor/bundle/ruby/2.1.0/bin/rspec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'rspec-core' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = ">= 0"

if ARGV.first
str = ARGV.first
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
version = $1
ARGV.shift
end
end

gem 'rspec-core', version
load Gem.bin_path('rspec-core', 'rspec', version)

loadメソッドにGem.bin_pathで生成されるものを渡している。

Gem.bin_path(‘rspec-core’, ‘rspec’, “>=0”)をpryで実行すると以下の文字列が取得できた。

1
=> "/Users/shoyan/Development/rails-sample/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.3.1/exe/rspec"

rspec-core-3.3.1/exe/rspecをみてみると、たった3行のコードがあるだけだった。

1
2
3
4
#!/usr/bin/env ruby

require 'rspec/core'
RSpec::Core::Runner.invoke

RSpec::Core::Runner.invokeはrspecを実行するトリガーのようだ。

rspec-core-3.3.1/lib/rspec/core/runner.rbをみてみる。

Provides the main entry point to run a suite of RSpec examples.

というコメントがあった。
Rspec examplesのスイートを実行する主要なエントリポイントを提供する と書いてある。

self.invokeをみると、disable_autorunとrunを実行して、runの戻り値が0でない場合はexitをrunの戻り値で実行している。

1
2
3
4
5
6
#Runs the suite of specs and exits the process with an appropriate exit
def self.invoke
disable_autorun!
status = run(ARGV, $stderr, $stdout).to_i
exit(status) if status != 0
end

ここで重要なのがrunなので、runの処理をおっていく。
runはself.runとrunがあるが、ここで実行されているのは、self.runのほう。

self.runはoptionsを取得して、それをnewして戻り値のインスタンスのrunメソッドを実行している。
ここでrunが実行される。

1
2
3
4
5
6
7
8
9
10
# Configures and runs a spec suite.
#
# @param err [IO] error stream
# @param out [IO] output stream
def run(err, out)
setup(err, out)
run_specs(@world.ordered_example_groups).tap do
persist_example_statuses
end
end

runメソッドはsetupとrun_specsを実行している。

setupはerror streamとoutput streamをセットしたり、オプションの設定をしたり、rspec関連のファイルを読み込んでいる。

run_specでテストを実行している。
run_specは全てのテストが成功したら0を返す。
テストに失敗したり何かしらの設定に失敗したら1を返す。