-
-
Save joescii/687ffe9090498a643ccf to your computer and use it in GitHub Desktop.
package com.joescii | |
package object code { | |
val DbThing = new DbThingImpl() | |
val HttpThing = new HttpThingImpl() | |
val BizThing = new BizThingImpl(DbThing, HttpThing) | |
trait DbThing { | |
def getDbStuff(params):Future[DbStuff] | |
} | |
trait HttpThing { | |
def getHttpStuff(params):Future[HttpStuff] | |
} | |
trait BizThing { | |
def doComplicatedStuff(params):Future[BizStuff] | |
} | |
private [code] class DbThingImpl extends DbThing { | |
override def getDbStuff(params):Future[DbStuff] = ??? | |
} | |
private [code] class HttpThingImpl extends HttpThing { | |
override def getHttpStuff(params):Future[HttpStuff] = ??? | |
} | |
private [code] class BizThingImpl(db:DbThing, http:HttpThing) extends BizThing { | |
override def doComplicatedStuff(params):Future[BizStuff] = | |
db.getDbStuff(params).zip(http.getHttpStuff(params)).map { | |
case (dbStuff, httpStuff) => ??? | |
} | |
} | |
} |
Something as simple as changing lines 31-33 to something more like
def f = (dbStuff:DbStuff, httpStuff: HttpStuff) => ???
private [code] class BizThingImpl(db:DbThing, http:HttpThing) {
override def doComplicatedStuff(params): Future[BizStuff] = {
db.getDbStuff(params).zip(http.getHttpStuff(params)).map(f)
}
}
Will help to separate your business logic from your concurrency logic for independent pure testing. What you probably care about actually testing for is in f
. This is what @mbbx6spp and I were getting at on Twitter. 💖
@pellunutty should
case class Env[M[_]](dbThing: DbThing[M], httpThing: HttpThing[M])
be
case class Env[M[_]](dbThing: M[DbThing], httpThing: M[HttpThing])
?
If not could you definite what DbThing and HttpThing would look like?
Thanks for all the tips! I feel like I'm actually in pretty good shape today, but with some cool ideas to try soon. I often hear about ReaderT
btw. Maybe it's about time to dive into that one.
I was going for something like what @sleepynate did. Just separate out the purecode (f) from the impure side-effecting code (BizThingImpl). That way you can unit test f easily and then integration test BizThingImpl with Wiremock etc.
@pellunutty's solution looks pretty nice. Because M is a Monad you could substitute Future with the Identity Monad for your tests (or any other Monad you want to).
This is essentially the same approach I described in http://logji.blogspot.com/2014/02/the-abstract-future.html, but that uses traits and the Cake pattern instead of ReaderT, and is consequently a bit more complicated, and initialization order can be a pain. I've come to the conclusion that ReaderT is a lot simpler to maintain.