Ввод фиктивных актеров в маршрут Spray для тестирования

Несколько групп в моем отделе начали использовать Spray для разработки веб-сервисов на основе REST, и все они сталкиваются с похожей проблемой, и на самом деле до сих пор не было хороших решений.

Предположим, у вас было следующее:

FooService extends Actor { ??? }

а потом в другом месте:

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)
    }
}

Другими словами, каждая конечная точка имеет соответствующего субъекта, и внутри маршрута субъект этого типа будет запущен в контексте запроса, ему будет передано сообщение, и этот субъект будет обрабатывать HttpResponse & stop.

У меня всегда были достаточно простые деревья маршрутов, которые я тестировал модулем только самими актерами, и позволял проводить тестирование маршрутов интеграционными тестами, но здесь я был отвергнут. Таким образом, проблема в том, что для модульных тестов люди хотят иметь возможность заменить FooService на MockFooService.

Есть ли стандартный способ справиться с этой ситуацией?

1 ответ

Решение

Я хотел бы пойти с шаблоном торта, где вы можете добавить реализацию в последний момент:

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

Я написал это в текстовом редакторе, поэтому я не уверен, что он компилируется.

Если вы хотите провести тестирование без актеров, вы можете извлечь эти две строки:

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

в какой-то метод и скрыть за этим актера. Например:

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

Этот метод может быть переопределен тем же способом, что и для тестов, и для тестов вы даже можете вообще не использовать актеров.

Другие вопросы по тегам