Reputation: 1371
for creating datasource
I have
object MyDataSource {
priavte lazy val dataSource: javax.sql.DataSource = {
val ds = new BasicDataSource
val conf = ConfigFactory.load()
val url = conf.getString("jdbc-url")
val driver = conf.getString("jdbc-driver")
val username = conf.getString("db-username")
val password = conf.getString("db-password")
val port = conf.getString("db-port")
val maxActive = conf.getInt("max-active")
val maxIdle = conf.getInt("max-idle")
val initSize = conf.getInt("init-size")
ds.setDriverClassName(driver)
ds.setUsername(username)
ds.setPassword(password)
ds.setMaxActive(maxActive)
ds.setMaxIdle(maxIdle)
ds.setInitialSize(initSize)
ds.setUrl(url)
ds
}
lazy val database = Database.forDataSource(dataSource)
}
MyDataSource
is used as below
def insertCompany = {
MyDataSource.database.withSession{ implicit session =>
company.insert(companyRow)
}
}
Now for testing I have trait DatabaseSpec
which loads test database(pointing to test db) config and has following fixture
def withSession(testCode: Session => Any) {
val session = postgres.createSession()
session.conn.setAutoCommit(false)
try {
testCode(session)
} finally {
session.rollback()
session.close()
}
}
And test code can then mix in DatabaseSpec
and use withSession
to test transactional code.
Now question is what's the best practice in keeping MyDataSource.database.withSession
abstracted away from DataSource
in insertCompany
so that method can be tested with DatabaseSpec
and pointing to test db?
Upvotes: 1
Views: 1061
Reputation: 11270
The best way to be able to exchange a value, .e.g for prod and testing is by parameterizing your code in that value. E.g.
def insertCompany(db: Database) = db.withSession(company.insert(companyRow)(_))
or
class DAO(db:Database){
def insertCompany = db.withSession(company.insert(companyRow)(_))
}
Keep it simple. Avoid unnecessary complexity like the Cake pattern, DI frameworks or mixin composition for this.
If you need to pass multiple values around... aggregate them into a "config"-class. Compose multiple config classes with different purposes to target different things, if you want to avoid writing one huge config class as stuff accumulates.
If you find yourself passing config objects to all your functions, you can mark them as implicit, that saves you at least the call-site code overhead. Or you can use something like scalaz's monadic function composition to avoid call site and definition site code overhead for passing config around. It is sometimes called the Reader monad, but it is simply for-comprehension enabled composition of 1-argument functions.
Slick 2.2 will ship with something like that out-of-the-box and make what you want very easy.
Also, here is an approach I am currently playing around with, a composable configuration object "TMap". This code example shows step by step how you get from global imports over parameterized functions and making them implicit to using TMap and removing most boilerplate: https://github.com/cvogt/slick-action/blob/0.1/src/test/scala/org/cvogt/di/TMapTest.scala#L49
Upvotes: 3