単純な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
}
おそらく、これが最も一般的なアプローチですが、それでも実行コンテキストを渡す必要があります。
それで、主な質問は、サービスの実装に依存しているが、それらのサービスの実際の実装がないエンドポイントをどのようにテストするかです。実装設計に問題があるのではないかと思いますが、それでも変更できます。質問:どのアプローチを選択する必要がありますか?
実行するには実行コンテキストが必要だと思うHttp().bindAndHandle(api, "host", 8080)
ので、別のクラスで実行します。
そうする場合は、アプローチ#3を使用し、mockito-scalaを使用して依存関係のモックを提供し、のインスタンスが完全に構築されRestAPI
たら、httpバインディングコードを移動した場所に渡すだけです。
要約すると、API定義をHttp Serverの初期化から分離し、依存関係をモックして満足してください。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加