I have a simple REST-API. Each api sub-path has its own service implementation.
The questuion is: how to correctly test it?
Example:
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
}
In this case i cannot test my endpoint because of dependency of execution context and service implementation which i should mock. I can create my own implementation of RestApi in test case, but i have to update it each time a change something inside real RestApi
I tried another way:
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
}
In this case, at least, i can test all endpoints but i have to pass execution context and build RestApi object before i can get my routes. Also, this is not the best solution because of now i need to write this new RestApi(dispatcher) extends StubOne with StubTwo with ...
and if there is 1 or 2 services - this is ok, but if there is more than 3, than it looks a bit awkward (in my opion).
Than i tried this approach:
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
}
Probably, it is the most common approach, but i still have to pass execution context.
So, the main question is how to test my endpoints which depend on service implementation but without real implementation of those services? I suspect that there is a problem in implementation design but i can still change it. The question it: which approach i should to choose?
Aucun commentaire:
Enregistrer un commentaire