3

Multiple groups in my department have started using Spray to develop REST based web services and are all running into a similar problem and there really haven't been great solutions to come out of it so far.

Suppose you had the following:

FooService extends Actor { ??? }

and then elsewhere:

path("SomePath") {
  id =>
    get {
      requestContext =>
        // I apologize for the janky Props usage here, just an example
        val fooService = actorRefFactory.actorOf(Props(new FooService(requestContext))) 
        queryService ! SomeMessage(id)
    }
}

In other words, every end point has a corresponding actor, and inside of a route an actor of that type will be spun up w/ the request context, a message will be passed to it and that actor will handle the HttpResponse & stop.

I've always had simple enough route trees that I've unit tested just the Actors themselves and let the route testing be handled by integration tests, but I've been overruled here. So the problem is that for unit tests people want to be able to replace FooService with a MockFooService

Is there a standard way of handling this situation?

geoffjentry
  • 4,674
  • 3
  • 31
  • 37

1 Answers1

3

I would go with a cake pattern where you can mix-in implementation at the last moment:

trait MyService extends HttpService with FooService {
  val route =
    path("SomePath") { id =>
        get { requestContext =>
            val fooService = actorRefFactory.actorOf(fooProps(requestContext)) 
            queryService ! SomeMessage(id)
        }
    }
}

trait FooService {
  def fooProps(requestContext: RequestContext): Props
}

trait TestFooService extends FooService {
  def fooProps(requestContext: RequestContext) =
    Props(new TestFooService(requestContext))
}

trait ProdFooService extends FooService {
  def fooProps(requestContext: RequestContext) =
    Props(new FooService(requestContext))
}

trait MyTestService extends MyService with TestFooService

trait MyProdService extends MyService with ProdFooService

I wrote it in a text editor so I'm not sure if it compiles.

If you want to do testing without actors at all you can extract these two lines:

val fooService = actorRefFactory.actorOf(fooProps(requestContext)) 
queryService ! SomeMessage(id)

into some method and hide an actor behind that. For instance:

def processRequest[T](msg: T): Unit = {
  // those 2 lines, maybe pass other args here too like context
}

This method can be overridden in the same cake pattern way and for tests you can even avoid using actors at all.

yǝsʞǝla
  • 16,272
  • 2
  • 44
  • 65
  • That's a nice way of doing it. Hiding the actors if possible is the way to go. – Oliver Shaw Mar 18 '15 at 09:40
  • So this is pretty close to what I tried to do and a bit more elegant, in a way which solves how I was trying to do things. For whatever reason I had ended up splitting the Props creating function into a separate trait tree altogether - thinking back I was trying to do something fancy at first & kept running into type erasure problems so by that point I'd lost the forest for the trees. Thanks, will make sure I didn't miss some subtlety in our base problem (some have other complexities but c'est la vie) – geoffjentry Mar 18 '15 at 13:07
  • 1
    it turns out that what I said was true and that my attempt to solve the hijinks lead itself to an overly complicated solution when what I wanted all along was *this*. There were a few outlier cases but I solved that via FP. All's right in the world. Thanks. – geoffjentry Mar 19 '15 at 04:59