I want to test routes with ScalatestRouteTest as follows:
trait MyRoutes extends Directives {
self: Api with ExecutionContextProvider =>
val myRoutes: Route =
pathPrefix("api") {
path("") {
(get & entity(as[MyState])) {
request => {
complete(doSomething(request.operation))
}
}
}
}
}
}
class RoutesSpec extends WordSpecLike with Api with ScalatestRouteTest
with Matchers with MyRoutes with MockitoSugar {
"The Routes" should {
"return status code success" in {
Get() ~> myRoutes ~> check {
status shouldEqual StatusCodes.Success
}
}
}
}
When running the test I get the runtime error:
Could not run test MyRoutesSpec: org.jboss.netty.channel.ChannelException: Failed to bind to: /127.0.0.1:2552
I don't want to bind to the localhost. How can this be accomplished?
The solution was to disable remoting and clustering (this was enabled in a separate configuration file) and use the default provider.
The actor remoting and clustering conflicts with the running application (started for the routing test). They pick up the same configuration and therefore both try to use the same port which conflicts.
The following code was added in trait MyRoutes to make it work:
// Quick hack: use a lazy val so that actor system can "instantiate" it
// in the overridden method in ScalatestRouteTest while the constructor
// of this class has not yet been called.
lazy val routeTestConfig =
"""
| akka.actor.provider = "akka.actor.LocalActorRefProvider"
| persistence.journal.plugin = "akka.persistence.journal.inmem"
""".stripMargin
override def createActorSystem(): ActorSystem =
ActorSystem("RouteTest", ConfigFactory.parseString(routeTestConfig))
Related
I am new to scala and I am using it with Playframework to build an API.
I have multiple docker containers (API, DB). Also, there is a module where DB configuration are specified. In this configuration, I use docker container name db in setJdbcUrl.
Everything works perfectly when running things within docker containers.
However, I have a healthcheck unit test that tests a route.
This test fails because it cant recognize the URL (as expected), but I am not sure if there is a way to update the fake application in the test to use the correct URL.
The Module
class ReadWriteDB extends ScalaModule {
#Singleton
#Provides
#Named("db.sql.readwrite.quill.context")
def quillContext(): PostgresJdbcContext[SnakeCase.type] = {
val ds = new HikariDataSource(DBConnectionConfig.hikariConfig())
QuillFactory.create(ds, Duration("2m"))
}
object DBConnectionConfig {
def hikariConfig(): HikariConfig = {
val config = new HikariConfig
config.setDriverClassName("org.postgresql.Driver")
config.setJdbcUrl("jdbc:postgresql://db:5432/postgres")
// config.setJdbcUrl("jdbc:postgresql://localhost:5432/postgres")
// with the commented config, unit test runs correctly
config.setUsername(...)
config.setPassword(...)
config
}
}
The unit test
class HealthCheckSpec extends PlaySpec with GuiceOneAppPerTest with Injecting {
"HealthCheck status" should {
"reply from the router" in {
// can I edit app to use a different URL here?
val request = FakeRequest(GET, "/status")
val home = route(app, request).get
status(home) mustBe OK
contentAsString(home) must include("API is running!")
}
}
}
Controller
#Singleton
class HealthCheck #Inject()(val controllerComponents: ControllerComponents) extends BaseController {
def check(): Action[AnyContent] = Action {
Ok("API is running!\n")
}
}
Any guidelines would be appreciated.
You have all the tools you need. I would turn the object into a class and use #provides and #singleton like you have in your quillContext, and I'd also pull out the behaviour into a trait and have a method for just is the healthcheck okay:
trait DBObjectConfig {
def hikariConfig(): HikariConfig
def isOK() :Boolean
}
#provides
class RealDBConnection extends DBObjectConfig {
def hikariConfig(): HikariConfig = {
val config = new HikariConfig
config.setDriverClassName("org.postgresql.Driver")
config.setJdbcUrl("jdbc:postgresql://db:5432/postgres")
config.setJdbcUrl("jdbc:postgresql://localhost:5432/postgres")
config.setUsername(...)
config.setPassword(...)
config
}
// todo: make something here that's a reliable test.
def isOK() :Boolean = {
Option(hakariConfig.getConnection()).isDefined
}
}
Your healthcheck controller can use isOK() to see if the db is up or not.
Then in your test, using Guice (your test extends an injector?) you should be able to bind a fake implementation of DBObjectConfig to your app, which will cause your controller to not instantiate a real db connection in the testing environment:
// inside your tests.
class MockDB extends DBObjectConfig {
def hikariConfig(): HikariConfig = ??? // implementation not required
def isOK() :Boolean = true // fake it being okay.
}
val application = new GuiceApplicationBuilder()
.overrides(bind[DBObjectConfig].to[MockDB])
.build()
Some technique using this general approach should work.
To implement Health Check I would recommend Play-Actuator dependency.
I am creating lagom simple application, with define one rest end point and hit end point using rest client postman. But In response I am getting, action not found error. I am integrate Akka with lagom, Following is my code:
Service:
trait TwitterSchedulerService extends Service {
def doWork: ServiceCall[NotUsed, Done]
override def descriptor: Descriptor = {
import Service._
named("scheduler").withCalls(
call(doWork)
)
}
}
ServiceImpl:
class TwitterSchedulerServiceImpl(system: ActorSystem) extends TwitterSchedulerService {
override def doWork = ServiceCall { _ =>
Future {
println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ")
Done
}
}
}
Loader Configuration :
class TwitterLoader extends LagomApplicationLoader {
override def load(context: LagomApplicationContext): LagomApplication =
new TwitterApplication(context) {
override def serviceLocator = NoServiceLocator
}
}
object TwitterSerializerRegistry extends JsonSerializerRegistry {
override val serializers = Vector(
JsonSerializer[String]
)
}
abstract class TwitterApplication(context: LagomApplicationContext) extends LagomApplication(context)
with CassandraPersistenceComponents with AhcWSComponents {
override lazy val lagomServer = LagomServer.forServices(
bindService[TwitterSchedulerService].to(wire[TwitterSchedulerServiceImpl])
)
override lazy val jsonSerializerRegistry = TwitterSerializerRegistry
}
I am following lagom documentation http://www.lagomframework.com/documentation/1.3.x/scala/Akka.html. I want to know, why this error occur, event all rest points are defined???
Your service is running at http://localhost:57211
http://localhost:9000 is running a Service Gateway server that acts as a reverse proxy to all of the services running in your project.
The Service Gateway can be configured to forward service calls onto your service, but it does not by default. You configure it by defining ACLs (access control lists) in your service descriptors.
Most commonly, you'll call withAutoAcl(true) to automatically forward all service call paths to your service:
trait TwitterSchedulerService extends Service {
def doWork: ServiceCall[NotUsed, Done]
override def descriptor: Descriptor = {
import Service._
named("scheduler").withCalls(
call(doWork)
).withAutoAcl(true)
}
}
If you want more control over which paths get forwarded from the Service Gateway to the back-end service, you can call withAcls to pass a list of explicit methods and path regular expressions that should be forwarded from the Service Gateway:
trait TwitterSchedulerService extends Service {
def doWork: ServiceCall[NotUsed, Done]
override def descriptor: Descriptor = {
import Service._
named("scheduler").withCalls(
call(doWork)
).withAcls(
ServiceAcl.forPathRegex("/doWork")
)
}
}
If you deploy to ConductR (part of the Lightbend Production Suite), these ACL configurations in your service descriptor are also used to generate ConductR ACL configuration.
I have the following multi-module project structure built using sbt:
myProject-api
myProject-core
myProject-core is organized as below:
It contains certain actors which acts as a facade to my services. For example., I have a UserActor that sits in front of a UserService. A NotificationActor that sits in front of a NotificationService and so on.
I have another trait that exposes there actors to anybody that is interested:
trait MyProjectCoreActors {
def myAppCfg = MyProjConfig.appCfg
def userActor = myAppCfg.userActor
def notifyActor = myAppCfg.notifyActor
}
object MyProjectCoreActors {
... some initialization routing that initializes the MyProjConfig
}
My UserActor is thus defined as:
class UserActor(service: UserService) extends Actor {
...
...
}
My UserService is as follows:
class UserService(dbConfig: DbConfig) {
...
...
}
I have another class called MyProjectConfig which I initialize using the application.conf file. In this file I have the connection details to the database and so on. The MyProjectConfig is initialized as below:
trait MyProjectConfig {
def actorSystem: ActorSystem
// the actors
def userActor: ActorRef
}
object MyProjectConfig {
def apply(appConfig: Config, system: ActorSystem): MyProjectConfig = {
new MyProjectConfig {
private val dbConfig = loadDBConfig(appConfig)
override val actorSystem = system
// each actor gets its own DBConfigInstance instance
override val userActor =
actorSystem.actorOf(
Props(new UserActor(UserService(dbConfig)))
)
}
}
}
I have now the Spray routing as defined below:
trait MyProjectService extends HttpService with MyProjectCoreActors {
def pingRoute = path("ping") {
get {
userActor ! "newUser"
complete("pong!")
}
}
def pongRoute = path("pong") {
get { complete("pong!?") }
}
def route = pingRoute ~ pongRoute
}
What is now missing is a way to call the MyProjectConfig.apply(....) method and pass in the Actor System and the underlying application.conf!
This was originally a Play based application, where I had a Lifecycle plug in which had access to the underlying Application from where I got the config and the actor system. How could I now get the same here with Spray?
I have a Boot class that looks like this:
object MyBootHttpService extends App {
implicit val actorSystem = ActorSystem("myproj-actor-system")
}
How could I pass this ActorSytem to MyProjectConfig.apply(....)? and from where could I get the application.conf?
I think you can do such things (DI) in your MyBootHttpService class.
For example
object MyBootHttpService extends App {
implicit val actorSystem = ActorSystem("myproj-actor-system")
private val config = ConfigFactory.load
private val myAppConfig = MyProjectConfig(config, actorSystem)
// Initialise classes that depend on config and actorsystem....
private val service = new MyProjectService with HttpServiceActor {
override implicit val actorRefFactory = actorSystem
}
// Bind our service
IO(Http) ? Bind(listener = service, interface = "0.0.0.0", port = config.getInt("port"))
}
Typesafe config library object ConfigFactory is generally used to load config files. ConfigFactory.load with no args will try and load application.conf from the classpath.
I have the following service:
trait PingService extends MyHttpService {
val pingRoutes =
path("ping") {
get {
complete("message" -> "pong")
}
}
}
MyHttpServiceis a custom class that extends HttpServiceand only contains utility methods.
This is the test spec:
import akka.actor.ActorRefFactory
import org.json4s.{DefaultFormats, Formats}
import org.scalatest.{FreeSpec, Matchers}
import spray.testkit.ScalatestRouteTest
class PingServiceSpec extends FreeSpec with PingService with ScalatestRouteTest with Matchers {
override implicit def actorRefFactory: ActorRefFactory = system
override implicit def json4sFormats: Formats = DefaultFormats
"Ping service" - {
"when calling GET /ping" - {
"should return 'pong'" in {
Get("/ping") ~> pingRoutes ~> check {
status should equal(200)
entity.asString should contain("pong")
}
}
}
}
}
Whenever I try to run the tests, I get the following error:
could not find implicit value for parameter ta: PingServiceSpec.this.TildeArrow[spray.routing.RequestContext,Unit]
Get("/ping") ~> userRoutes ~> check {
^
Am I doing something stupid? Any kind of help will be appreciated!
EDIT: Although this might look like a dupe of this question, it's not.
The solution provided in that post it's not working.
The ScalatestRouteTest already provides an implicit ActorSystem. Remove the implicit modifier from your actorRefFactory method and the test should get executed.
I looked at spray 1.3.1 testkit documentation but could not find a proper example for what I need below:
I have this sample spray 1.3.1 service
trait MyService extends HttpServiceActor {
def receive = runRoute(routes)
val isAliveRoute = path("isalive") {
get {
complete("YES")
}
}
val routes = isAliveRoute
}
I'm trying to test it with spray test-kit but failing to do so here is my TestCase
#RunWith(classOf[JUnitRunner])
class MyServiceTest extends FlatSpec with ScalatestRouteTest with ShouldMatchers with MyService {
"The service" should "return a greeting for GET requests to the isalive" in {
Get() ~> isAliveRoute ~> check {
responseAs[String] should be("YES")
}
}
}
However I get
Error:(15, 87) illegal inheritance; superclass FlatSpec is not a
subclass of the superclass HttpServiceActor of the mixin trait
MyService class MyServiceTest extends FlatSpec with ScalatestRouteTest
with ShouldMatchers with MyService {
^
^
and:
Error:(17, 11) could not find implicit value for parameter ta:
MyServiceTest.this.TildeArrow[spray.routing.RequestContext,Unit]
Get() ~> isAliveRoute ~> check {
^
Are there ways around this?
Can I have my service extend HttpServiceActor and still be able to test it with scalatest and spray testkit? if so how? I want to continue extending HttpServiceActor makes life easier and code more compact and readable. But I would also like to test it with scalatest.
so i tried updating the code as comment said to split to trait and service as in:
https://github.com/spray/spray-template/blob/on_spray-can_1.1/src/main/scala/com/example/MyService.scala
class MyServiceActor extends Actor with MyService {
def actorRefFactory = context
def receive = runRoute(routes)
}
trait MyService extends HttpService {
val isAliveRoute = path("isalive") {
get {
complete("OK")
}
}
val routes = isAliveRoute
}
#RunWith(classOf[JUnitRunner])
class MyServiceTest extends FlatSpec with ShouldMatchers with MyService with ScalatestRouteTest {
def actorRefFactory = system
"The service" should "return a greeting for GET requests to the isalive" in {
Get() ~> isAliveRoute ~> check {
responseAs[String] should be("YES")
}
}
}
but i get:
Testing started at 13:26 ... [DEBUG] [05/14/2014 13:26:25.813]
[ScalaTest-run]
[EventStream(akka://com-server-web-conf-MyServiceTest)] logger
log1-Logging$DefaultLogger started [DEBUG] [05/14/2014 13:26:25.814]
[ScalaTest-run]
[EventStream(akka://com-server-web-conf-MyServiceTest)] Default
Loggers started Request was not handled
org.scalatest.exceptions.TestFailedException: Request was not handled
at
spray.testkit.ScalatestInterface$class.failTest(ScalatestInterface.scala:25)
at
I had similar problem with one difference. At complete statement I had sending message to another actor, so I needed actor functionality to test behavior. I solved it that way:
trait MyService extends HttpService {
val myActor: ActorRef
val homeS: ActorRef
(...)
and sending message inside get to
path("isalive") { get {
ctx: RequestContext => homeS.tell(ctx, myActor ) }
//on homeS actor:
def receive = {
case ctx: RequestContext =>
ctx.complete( ... )
but if you don't need actor functionality of in MyService then better is to do like #jrudolph said in comment.
Full code here: https://github.com/kastoestoramadus/simple_zookeeper.git