2017-06-20 67 views
1

我有一個通過Http().cachedHostConnectionPoolHttps與第三方服務集成的akka​​-http應用中的路由。我想以正確的方式測試它。但是不知道應該如何:(Akka Http:如何測試流量到第三方服務的路由?

下面是這條路線的樣子:

val routes: Route = pathPrefix("access-tokens") { 
    pathPrefix(Segment) { userId => 
    parameters('refreshToken) { refreshToken => 
     onSuccess(accessTokenActor ? GetAccessToken(userId, refreshToken)) { 
     case token: AccessToken => complete(ok(token.toJson)) 
     case AccessTokenError => complete(internalServerError("There was problems while retriving the access token")) 
     } 
    } 
    } 
} 

這條路線的背後隱藏accessTokenActor所有邏輯發生了,那就是:

class AccessTokenActor extends Actor with ActorLogging with APIConfig { 

    implicit val actorSystem = context.system 
    import context.dispatcher 
    implicit val materializer = ActorMaterializer() 

    import AccessTokenActor._ 

    val connectionFlow = Http().cachedHostConnectionPoolHttps[String]("www.service.token.provider.com") 

    override def receive: Receive = { 
    case get: GetAccessToken => { 
     val senderActor = sender() 
     Source.fromFuture(Future.successful(
      HttpRequest(
      HttpMethods.GET, 
      "/oauth2/token", 
      Nil, 
      FormData(Map(
       "clientId" -> youtubeClientId,"clientSecret" -> youtubeSecret,"refreshToken" -> get.refreshToken)) 
       .toEntity(HttpCharsets.`UTF-8`)) -> get.channelId 
     ) 
     ) 
     .via(connectionFlow) 
     .map { 
      case (Success(resp), id) => resp.status match { 
      case StatusCodes.OK => Unmarshal(resp.entity).to[AccessTokenModel] 
       .map(senderActor ! AccessToken(_.access_token)) 
      case _ => senderActor ! AccessTokenError 
      } 
      case _ => senderActor ! AccessTokenError 
     } 
    }.runWith(Sink.head) 
    case _ => log.info("Unknown message") 
    } 

    } 

所以問題是如何測試這條路線更好,請記住帶有流的演員也存在其中。

+0

在您的測試中實例化一個模擬網頁http服務器並接聽電話? –

+0

@DiegoMartinoia是的,這是最明顯的例子。如果在這種情況下沒有別的方法可行,我會去做。其實我正在尋找一些阿卡流測試技術。類似於用假冒流替換......你對此有何看法? –

+0

依賴注入和模擬的事情是,如果「真實」依賴項被破壞,那麼測試不會失敗。我在這方面是少數,但特別是當涉及到像遠程http調用這樣複雜的事情時,我喜歡黑盒子的東西。特別是Akka應用程序通常需要測試很多「奇怪」的東西(持久性,分片,集羣),我發現實際上旋轉應用程序並在兩端進行探測變得更容易。但那就是我 –

回答

3

Compo sition

測試路由邏輯的一個難點就是,目前很難分離功能。在沒有Actor的情況下測試Route邏輯是不可能的,並且很難在沒有路由的情況下測試您的Actor查詢。

我想你會更好地服務於函數組合,這樣你就可以分離出你想要測試的東西。

首先抽象掉Actor查詢(問):

sealed trait TokenResponse 
case class AccessToken() extends TokenResponse {...} 
case object AccessTokenError extends TokenResponse 

val queryActorForToken : (ActorRef) => (GetAccessToken) => Future[TokenResponse] = 
    (ref) => (getAccessToken) => (ref ? getAccessToken).mapTo[TokenResponse] 

現在將您routes值轉換成高階方法,需要在查詢函數作爲參數:

val actorRef : ActorRef = ??? //not shown in question 

type TokenQuery = GetAccessToken => Future[TokenResponse] 

val actorTokenQuery : TokenQuery = queryActorForToken(actorRef) 

val errorMsg = "There was problems while retriving the access token" 

def createRoute(getToken : TokenQuery = actorTokenQuery) : Route = 
    pathPrefix("access-tokens") { 
    pathPrefix(Segment) { userId => 
     parameters('refreshToken) { refreshToken => 
     onSuccess(getToken(GetAccessToken(userId, refreshToken))) { 
      case token: AccessToken => complete(ok(token.toJson)) 
      case AccessTokenError => complete(internalServerError(errorMsg)) 
     } 
     } 
    } 
    } 

//original routes 
val routes = createRoute() 

測試

現在你可以測試queryActorForToken而不需要一個Route,你可以在不需要演員的情況下測試createRoute方法!

您可以用注射的功能,它總是返回一個預先定義的標記測試createRoute:

val testToken : AccessToken = ??? 

val alwaysSuccceedsRoute = createRoute(_ => Success(testToken)) 

Get("/access-tokens/fooUser?refreshToken=bar" ~> alwaysSucceedsRoute ~> check { 
    status shouldEqual StatusCodes.Ok 
    responseAs[String] shouldEqual testToken.toJson 
} 

或者,你也可以通過注入功能,從來沒有返回一個標記測試createRoute:

val alwaysFailsRoute = createRoute(_ => Success(AccessTokenError)) 

Get("/access-tokens/fooUser?refreshToken=bar" ~> alwaysFailsRoute ~> check { 
    status shouldEqual StatusCodes.InternalServerError 
    responseAs[String] shouldEqual errorMsg 
} 
+0

真棒方法:)到一天結束時,我明白我的錯誤是將所有的東西放在一起。我需要有更多獨立的組件! –