Ввод фиктивных актеров в маршрут 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
}
Этот метод может быть переопределен тем же способом, что и для тестов, и для тестов вы даже можете вообще не использовать актеров.