ScalaのコードをRSpecでテストする
ついったーでspecsについても教えてもらったけど、JavaをRSpecできるならScalaも可能なのではないかということでRSpecでScalaのBDDにチャレンジしてみた。
環境の準備
JRuby入れる
公式から落としてきて/opt/jrubyに放り込む。
そして/opt/jruby/binにパスを通す。
ディレクトリ構造
CLASSPATHの設定に必要となるので整理。
scala_with_rspec/
build/
classes/
spec/
src/
build/classes以下にscalacでコンパイルされたclassファイルが出力される。
spec以下にはspecファイルを置き、srcにScalaのソースを置く。
以下ずっとscala_with_rspec直下での作業。
CLASSPATHの設定
1. scala-library.jarを含める /opt/local/scala-2.8.0.final/lib/scala-library.jar
2. テスト対象のclassファイルが存在するディレクトリを含める
ということなので以下のようなコマンドを実行。
$ export CLASSPATH=$CLASSPATH:/opt/local/scala-2.8.0.final/lib/scala-library.jar:/path/to/scala_with_rspec/build/classes
※ /path/to/scala_with_rspec/build/classesはあとでRakefileの中で指定するようにした。
ScalaをRSpecで
さてspecと実装を書いてテストを実行してみる。
単純なゲッターとセッターのテスト。
sample_spec.rb
require 'java'
module ToBrassWithspec
include_package "to.brass.withspec"
end
describe ToBrassWithspec::Sample, "は" do
before(:each) do
@sample = ToBrassWithspec::Sample.new
end
it "名前を取得できること" do
@sample.get_name().should == 'nanashi-san'
end
it "名前を設定できること" do
@sample.set_name('hoge')
@sample.get_name.should == 'hoge'
end
end
アンダースコア化したメソッド名も使えるのがいい!
Sample.scala
package to.brass.withspec
class Sample() {
var name: String = "nanashi-san"
def setName(newName: String): Unit = {
name = newName
}
def getName(): String = name
}
ちなみにこのようなゲッターとセッターをScalaで書く必要は本来全くないみたい。
まああくまで実験サンプルということで。
コンパイル
あらかじめコンパイルしないとテストできないのでコンパイルしておく。
scalac -d build/classes src/Sample.scala
実行
コンパイルできたらテスト。
以下コマンドと出力。
$ jruby -S spec spec/sample_spec.rb
..
Finished in 0.2 seconds
JRubyの起動でちょっと待たされるのがやや残念。
でも通ったー!
Rakefile
コンパイルとspecの実行を別々のコマンドでやっていると、コンパイルを忘れてspecを走らせるという愚をしばしば犯しそうなので、クラス名を指定してScalaのコンパイルとRSpecの実行を行うRakefileを書いてみた。
require 'rake'
require 'rubygems'
require 'active_support/inflector'
task :default => :spec
desc "compile scala and run rspec."
task :spec do
sh "scalac -d #{app_class_path} src/#{ENV['classname'].camelize}.scala"
sh "jruby -S spec spec/#{ENV['classname'].underscore}_spec.rb"
end
def app_class_path
File.join(File.dirname(__FILE__), 'build', 'classes')
end
以下のように実行。
$ rake spec classname=sample
本格的に使おうとするとまだ実用には耐えないだろうけど、あとは必要に応じていじってみよう。
俺たちのScalaはこれからだ!
参考
2010-09-20追記
Rakefileの中でENV[“CLASSPATH”]を設定する方法だとうまくいかなくなってしまった。
ということで削除。
それとも前うまくいったのはシェルでやったCLASSPATHの設定が残ってただけで、もともとうまくいかなかったのかな・・・
Rails3にしたりとかRVM入れ直したりとかいろいろ環境いじったのでよくわからない。
あとcamelizeを使うためのactive_supportのrequireの仕方もActiveSupport 3.0から変わったので修正した。
2010-09-20更に追記
「作ったクラスの最初の利用者になる」という観点から見ると実コードの言語とテストコードの言語を別々にすると言うのはイマイチだった。
RSpecのExample内で十分使いやすいと思ったメソッドを実際に(JavaやScalaで)使うともう一息使い勝手が悪かったり、またテストコードでDuck Typingを使ってしまうとRSpecは問題なく通るのに実際に使うと型に関するエラーが出たりしてガッカリ。
この技は宴会芸にとどめておくべきなのかもしれない。