今週のRuby Weeklyで TestRocket というテスティングライブラリが紹介されていたので、試しに使ってみました。
対象となるバージョン
- testrocket 1.0.0
セットアップ
gemをインストールすればOKです。
gem install testrocket
Bundlerを使う場合は、Gemfileに追加してbundle install
してください。
gem 'testrocket'
使い方
testrocket は Refinements を利用するので、次のようにテストを書きたいファイルでrequire&usingします。
# テストを書きたいファイルでrequire&using
require 'testrocket'
using TestRocket
Fizz Buzzプログラムをテストするならこんな感じになります。
def fizz_buzz(n)
if n % 15 == 0
'Fizz Buzz'
elsif n % 3 == 0
'Fizz'
elsif n % 5 == 0
'Buzz'
else
n.to_s
end
end
require 'testrocket'
using TestRocket
+-> { fizz_buzz(1) == '1' }
+-> { fizz_buzz(2) == '2' }
+-> { fizz_buzz(3) == 'Fizz' }
+-> { fizz_buzz(4) == '4' }
+-> { fizz_buzz(5) == 'Buzz' }
+-> { fizz_buzz(6) == 'Fizz' }
+-> { fizz_buzz(15) == 'Fizz Buzz' }
実行してみましょう。
$ ruby fizz_buzz.rb
OK
OK
OK
OK
OK
OK
OK
はい、実行できました。
試しに失敗させてみましょう。
+-> { fizz_buzz(1) == 'Hoge' }
$ ruby fizz_buzz.rb
FAIL @ fizz_buzz.rb:16
OK
OK
OK
OK
OK
OK
OK
こんな感じで失敗します。
なお、テスト結果のサマリ(何件パスして、何件失敗したか、といった情報)は表示されないようです。
構文
テストを書くための構文は必要最小限です。
+-> {}
でブロックが真を返せばパスするテストを、--> {}
でブロックが偽を返すか、例外が発生したらパスするテストを書きます。
# ブロックが真を返せばパス
+-> { 1 + 1 == 2 }
# ブロックが偽を返すか、例外が発生したらパス
--> { 'abc'.nil? }
--> { 1 / 0 }
~-> {}
でpendingするテストが書けます。
# このテストはpending
~-> { 1 + 1 == 2 }
$ ruby sample.rb
PENDING 'true' @ sample.rb:4
!-> {}
はテストの説明(description)を書くために使います。
# テストの説明
!-> { "test fizz buzz" }
# テスト本体
+-> { fizz_buzz(1) == '1' }
$ ruby fizz_buzz.rb
FIRE 'test fizz buzz'!
OK
用意されている構文はこれだけです。
応用
READMEの中ではクラスファイルの中でクラスの読み込みと同時にテストを実行する例が示されています。
require 'testrocket'
using TestRocket
class User
def initialize(name:, age:)
@name = name
@age = age
end
def greet
if @age <= 12
"ぼくは#{@name}だよ。"
else
"僕は#{@name}です。"
end
end
# クラスファイル読み込み時にgreetメソッドのテストを実行する
+-> { User.new(name: 'たろう', age: 12).greet == 'ぼくはたろうだよ。' }
+-> { User.new(name: 'たろう', age: 13).greet == '僕はたろうです。' }
end
$ ruby user.rb
OK
OK
ただし、ENV['RACK_ENV']
かENV['RAILS_ENV']
が"production"を返したり、Rails.env.production?
がtrue
になる場合はテストを実行しません。(つまり、本番環境ではテストは実行されません)
また、テストを実行するためには、対象となるメソッドがすでに定義されている必要があるので、greet
メソッドよりも手前にテストを書くとテストが失敗します。
# この時点ではgreetメソッドが定義されていないので、テストが失敗する
+-> { User.new(name: 'たろう', age: 12).greet == 'ぼくはたろうだよ。' }
+-> { User.new(name: 'たろう', age: 13).greet == '僕はたろうです。' }
def greet
if @age <= 12
"ぼくは#{@name}だよ。"
else
"僕は#{@name}です。"
end
end
$ ruby user.rb
FAIL @ user.rb:10
FAIL @ user.rb:11
技術的な解説
+->{}
や-->{}
はProcオブジェクトの単項演算子として定義されています。
つまり、Rubyの文法的には、+3
や-2
と同じように、プラスのProcオブジェクト(+-> {}
)や、マイナスのProcオブジェクト(--> {}
)を書いていることになります。
ソースコードも超シンプル!
testrocket 1.0.0のソースコードは1ファイルのみで、わずか37行です。
実装も大変シンプルなので、コードリーディングの題材としてみるのも面白いかもしれません。
まとめ
というわけで、この記事では超シンプルなテスティングライブラリ、TestRocketを紹介してみました。
これでRailsアプリのテストを書け、と言われるとかなり難しいと思いますが、ごく単純なプログラミング問題をテスト駆動開発したりする場合は便利かもしれません。
興味を持った人はTestRocketで一度遊んでみてください!