scala objectのMock その2

どうもJMockitではscalaのobjectをモック化できないようなので、scalaベースのscalamockを試してみる。scalamockはsbt, scalatestの使用を前提としているため、sbtでプロジェクトを作る。プロジェクトの構成がsbtの構成になってしまうので、これもちょっと困る。環境構築に必要なファイルは以下

$PROJECT_ROOT/build.sbt

name := "test.sbt"

version := "1.0"

scalaVersion := "2.9.2"

libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "1.8" % "test",
"org.scalamock" %% "scalamock-scalatest-support" % "2.3",
"junit" % "junit" % "latest.integration" % "test",
"commons-logging" % "commons-logging" % "1.1.1"
)

$PROJECT_ROOT/project/Build.scala

import sbt._
import Keys._
import ScalaMockPlugin._

object MyProject extends Build {
override lazy val settings = super.settings ++ Seq(
organization := "",
version := "1.0",
scalaVersion := "2.9.2",
libraryDependencies += "org.scalamock" %% "scalamock-scalatest-support" % "2.4",
autoCompilerPlugins := true,
addCompilerPlugin("org.scalamock" %% "scalamock-compiler-plugin" % "2.4")
)

lazy val myproject = Project("MyProject", file(".")) settings(generateMocksSettings: _*) configs(Mock)

$PROJECT_ROOT/project/plugins.sbt

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.1")

$PROJECT_ROOT/project/project/PluginDef.scala

import sbt._
object PluginDef extends Build {
override lazy val projects = Seq(root)
lazy val root = Project("plugins", file(".")) dependsOn(scalamockPlugin)
lazy val scalamockPlugin = uri("git://github.com/paulbutcher/scalamock-sbt-plugin")
}

これで環境はできた。sbt elipseなどしてeclipseでもインポートできるようにする。テストのターゲットクラスは以下

package test2

class Boo
object Boo {
def foo = {
println("Boo#foo called")
true
}
}

$PROJECT_ROOT/src/generate-mocks/scala/GenerateMocks.scala

import org.scalamock.annotation.mockObject

class GenerateMocks {

@mockObject(Boo) class BooMock

}

として、sbt generate-mocksする。するとモックオブジェクトができあがる。次にテストコード

import org.scalatest.FunSuite
import org.scalamock.scalatest.MockFactory
import org.scalamock.generated.GeneratedMockFactory
import org.scalamock.scalatest.MockFactory

class BooTest {

test("Boo#foo 通常動作") {
assert(Boo.foo)
}

test("Boo#foo モックを使って結果を変える") {
val boo = mockObject(Boo)
boo.expects.foo() returning ({println("BooMock#foo called");false})

assert(! Boo.foo)
}

}

このとき、eclipseだと、org.scalamock.generated.GeneratedMockFactoryが見えなくてエラーになってしまう。ビルドパスのソースフォルダに、target/scala-2.9.2/src_managed/test/scalaを追加すると解決。sbt eclipseがやってくれるといいがやってくれない。sbt eclipseを実行するとこの設定はなくなってしまうので要注意。

ここまでできたらsbt testとすれば

Boo#foo called
BooMock#foo called
[info] BooTest:
[info] - Boo#foo 通常動作
[info] - Boo#foo モックを使って結果を変える
[info] Passed: : Total 4, Failed 0, Errors 0, Passed 4, Skipped 0
[success] Total time: 2 s, completed 2013/09/06 18:51:24

となってモックが呼ばれていることがわかる。