dependency injection - How would I inject a mocked singleton object in Scala? -
we're using scala 2.10.2, , we're using slick 1.0.1 our daos. we're trying mock daos scalamock, , i'm trying figure out way inject mocked daos. i've used java several years, i've started using scala 2 weeks ago.
right our code looks (ignore syntax errors, i've condensed code without making sure still satisfies type system)
abstract class richtable[t](name: string) extends slick.driver.mysqldriver.simple.table[t](name) { type itemtype = t def id = column[int]("id", o.primarykey, o.autoinc) ... } object users extends richtable[user]("users") { def crypted_password = column[string]("crypted_password") ... } case class user(id: option[int] = none, crypted_password: string) { def updatepassword(...) = { users.where(_.id === id).map{e => e.crypted_password}.update("asdf") } } all of daos singleton objects inheriting richtable[t]
we'd able mock users , other singleton dao objects - right of our unit tests hitting database. however, problem we're running how inject mock singleton objects. solution we've come far is:
object daorepo { var usersdao : users.type = users var anotherdao : another.type = ... } object users extends richtable[user]("users") { def apply() : users.type = daorepos.usersdao } def updatepassword(...) = { users().where(_.id === id).map{e => e.crypted_password}.update("asdf") } def test = { val mockusers = mock[users] daorepo.usersdao = mockusers // run test using mock repo } we're changing of our references users users(), doesn't add excessive amount of clutter. however, use of vars in daorepo smells bad, , i'm wondering if has suggestion improve this.
i've read real-world scala: dependency injection (di) , component based dependency injection in scala - think understand how use traits compose daorepo, like
trait usersrepo { val usersdao : users.type = users } trait daorepo extends usersrepo anotherrepo { } trait userstestrepo { val usersdao : users.type = mock[users] } but still don't understand how i'd inject new trait. like
class daorepoimpl extends daorepo { } object daowrapper { var repo : daorepo = new daorepoimpl } def test = { daowrapper.repo = new daorepoimpl userstestrepo } which replaces 2 dozen vars in object daorepo single var in object daowrapper, seems there ought clean way without vars.
i don't understand classes , traits.
trait usersrepo { val usersdao : users.type = users } trait anotherrepo { val anotherdao : another.type = } trait daorepo extends usersrepo anotherrepo and can instantiate real realdaorepo
object realdaorepo extends daorepo { } or mocked one
object mockeddaorepo extends daorepo { override val usersdao : users.type = mock[users] override val anotherdao : another.type = mock[another] } then inject daorepo in application can use cake pattern , self type references that.
i'll publish article on infoq fr helps spring people understand cake pattern. here's code sample article:
trait usertweetservicecomponent { val usertweetservice: usertweetservice } trait usertweetservice { def createuser(user: user): user def createtweet(tweet: tweet): tweet def getuser(id: string): user def gettweet(id: string): tweet def getuserandtweets(id: string): (user,list[tweet]) } trait defaultusertweetservicecomponent extends usertweetservicecomponent { // declare dependencies of service here self: userrepositorycomponent tweetrepositorycomponent => override val usertweetservice: usertweetservice = new defaultusertweetservice class defaultusertweetservice extends usertweetservice { override def createuser(user: user): user = userrepository.createuser(user) override def createtweet(tweet: tweet): tweet = tweetrepository.createtweet(tweet) override def getuser(id: string): user = userrepository.getuser(id) override def gettweet(id: string): tweet = tweetrepository.gettweet(id) override def getuserandtweets(id: string): (user,list[tweet]) = { val user = userrepository.getuser(id) val tweets = tweetrepository.getallbyuser(user) (user,tweets) } } } note same spring declaration:
<bean name="usertweetservice" class="service.impl.defaultusertweetservice"> <property name="userrepository" ref="userrepository"/> <property name="tweetrepository" ref="tweetrepository"/> </bean> and when do:
trait myapplicationmixin extends defaultusertweetservicecomponent inmemoryuserrepositorycomponent inmemorytweetrepositorycomponent it's same spring declaration (but typesafe application context):
<import resource="classpath*:/meta-inf/application-context-default-tweet-services.xml" /> <import resource="classpath*:/meta-inf/application-context-inmemory-tweet-repository.xml" /> <import resource="classpath*:/meta-inf/application-context-inmemory-user-repository.xml" /> then can use app with:
val app = new myapplicationmixin { } or
val app = new myapplicationmixin { override val tweetrepository = mock[tweetrepository] } the latter same spring bean override:
<import resource="classpath*:/meta-inf/application-context-default-tweet-services.xml" /> <import resource="classpath*:/meta-inf/application-context-inmemory-tweet-repository.xml" /> <import resource="classpath*:/meta-inf/application-context-inmemory-user-repository.xml" /> <!-- bean override 1 defined in application-context-inmemory-tweet-repository.xml notice spring isn't helpful declare behavior of mock, easier cake pattern since directly write code --> <bean id="tweetrepository" class="repository.impl.mockedtweetrepository"/> so come problem, use cake pattern , create service components in application, depend on daorepo trait.
and can do:
trait myapplicationmixin extends defaultuserservicecomponent anotherservicecomponent daorepo and then:
val app = new myapplicationmixin { } or
val app = new myapplicationmixin { override val usersdao : users.type = mock[users] override val anotherdao : another.type = mock[another] } once application built can use that:
app.userservice.createuser(...) the application built application context
Comments
Post a Comment