Outline
ScalaでDIをちゃんと考えてみようと思い、ここを参考にcakePatternとか色々調べてみました。
http://eed3si9n.com/ja/real-world-scala-dependency-injection-di
でも全体的に何が嬉しいのか見えなかったので、手段から入るのをやめて、
何ができれば嬉しいのかを明確にしてから一番シンプルにそれが達成できそうなものを考えてみました。
Premise
SpringMVCの設計みたく、
Controller→Service→Repository
の依存関係があるとします。
また、今回はRDBを扱い、トランザクションの管理はServiceでやるとします。
Goal
・各Objectで、mockを差し込んだ単体テストが容易に行える。
・UI確認用に、configを書き換えると実行時にMockのServiceが動くようにする。
・(上記を実現しつつ、ボイラ−プレートが少なく、シンプルな構成になるように。)
How
lazy valで定義したフィールドにMockをoverrideする。
Code
class MyController {
lazy val myService:MyService = MyFactory.getMyService()
def action() = println(myService.process())
}
object MyFactory{
def getMyService() = {
new MyServiceImpl
// if(Config.mode = "Prod"){
// new MyServiceImpl
// }else{
// new MyServiceMock
// }
}
}
trait MyService {
def process(): String
}
class MyServiceImpl extends MyService{
lazy val pool = new ConnectionPool
lazy val myDao = new MyRepository
override def process() = {
implicit val con = pool.getConnection()
myDao.fetchData
}
}
class MyServiceMock extends MyService{
override def process() = {
"mock"
}
}
class MyRepository {
def fetchData(implicit dbCon: String): String = {
"aa from " + dbCon
}
}
class ConnectionPool{
def getConnection() = "real Connection"
}
本番時はこのように。
object Main {
def main(args: Array[String]) {
val controller = new MyController()
controller.action()
}
}
テスト時はこのように。
class Test extends FunSuite{
test("testMyController") {
val controller = new MyController(){override lazy val myService = new MyService {
override def process(): String = "mock"
}}
controller.action()
}
test("testMyServiceImpl") {
val mockDao = new MyRepository {
override def fetchData(implicit dbConnection: String): String = "mock Fetch"
}
val poolMock = new ConnectionPool {
override def getConnection() = "mock Fetch"
}
val service = new MyServiceImpl{
override lazy val pool = poolMock
override lazy val myDao = mockDao
}
assert(service.process() == "mock Fetch")
}
test("testMyDaoImpl") {
val repo = new MyRepository
val mockDBCon = "mock"
assert(repo.fetchData(mockDBCon) == "aa from mock")
}
}
Conclusion
具象クラスのコンストラクタに依存するのがキモい。。
でもそれ以外は問題ないのでは?
cakePatternとかはどういう時に真価を発揮するのかとか、誰かマサカリください。