Как спроектировать api rest используя akka-http, чтобы его было легко тестировать? - PullRequest
0 голосов
/ 28 ноября 2018

У меня простой REST-API.У каждого подпути api есть своя собственная реализация сервиса.

Вопрос: как правильно его проверить?

Пример:

class RestAPI(implicit dispatcher: ExecutionContext)  // some services need its own dispatcher
extends FirstService with SecondService with etc... { 
  val api: Route = pathPrefix("api") {
    get {
      firstService()
    } ~ post {
      secondService()
    } ~ ...
  }

  def start(implicit system: ActorSystem, materializer: ActorMaterializer): Unit = {
    Http().bindAndHandle(api, "host", 8080)
  }
}

object RestAPI {
  def apply(implicit dispatcher: ExecutionContext): RestAPI = new RestAPI
}

В этом случае я не могу проверитьмоя конечная точка из-за зависимости контекста исполнения и реализации сервиса, которую я должен издеваться.Я могу создать свою собственную реализацию RestApi в тестовом примере, но мне приходится обновлять ее каждый раз, когда что-то меняется внутри реального RestApi

Я пробовал другой способ:

class RestAPI(implicit dispatcher: ExecutionContext)  { // some services need its own dispatcher
  this: FirstService with SecondService with etc... =>
  val api: Route = pathPrefix("api") {
    get {
      firstService()
    } ~ post {
      secondService()
    } ~ ...
  }

  def start(implicit system: ActorSystem, materializer: ActorMaterializer): Unit = {
    Http().bindAndHandle(api, "host", 8080)
  }
}

object RestAPI {
  def apply(implicit dispatcher: ExecutionContext): RestAPI = new RestAPI extends DefaultFirstService with DefaultSecondService with etc...
}


Test {
  val api = (new RestApi(dispatcher) extends StubOne with StubTwo with ...).api
}

В этом случаепо крайней мере, я могу проверить все конечные точки, но мне нужно пройти контекст выполнения и построить объект RestApi, прежде чем я смогу получить свои маршруты.Кроме того, это не лучшее решение, потому что сейчас мне нужно написать new RestApi(dispatcher) extends StubOne with StubTwo with ..., и если есть 1 или 2 службы - это нормально, но если их больше 3, это выглядит немного неловко (по моему мнению).

Тогда я попробовал этот подход:

class RestAPI(serviceOne: FirstService, serviceTwo: SecondService, ...)(implicit dispatcher: ExecutionContext)  { // some services need its own dispatcher
  val api: Route = pathPrefix("api") {
    get {
      serviceOne.firstService()
    } ~ post {
      serviceTwo.secondService()
    } ~ ...
  }

  def start(implicit system: ActorSystem, materializer: ActorMaterializer): Unit = {
    Http().bindAndHandle(api, "host", 8080)
  }
}

object RestAPI {
  def apply(serviceOne: FirstService, serviceTwo: SecondService, ...)(implicit dispatcher: ExecutionContext): RestAPI = new RestAPI(serviceOne, serviceTwo, ...)
}


Test {
  val api = (new RestApi(...)(dispatcher)).api
}

Вероятно, это наиболее распространенный подход, но мне все равно нужно передать контекст выполнения.

Итак, главный вопрос - как проверить мои конечные точки, которые зависят от реализации сервиса, но без реальной реализации этих сервисов?Я подозреваю, что есть проблема в дизайне реализации, но я все еще могу ее изменить.Вопрос в том, какой подход выбрать?

1 Ответ

0 голосов
/ 30 ноября 2018

Полагаю, вам нужен контекст выполнения для запуска Http().bindAndHandle(api, "host", 8080), поэтому я бы просто сделал это в другом классе.

Если вы сделаете это, вы можете использовать свой подход # 3, использовать mockito-scala , чтобы предоставить имитации ваших зависимостей, и когда у вас есть полностью сконструированный экземпляр RestAPI, просто передайте его туда, куда вы переместили код привязки http, и вот оно.

Подводя итог, отделите определение API от инициализации Http-сервера, смейтесь над своими зависимостями и будьте счастливы!

...