How to start Play application before tests and then shut it down in specs2? - scala

My goal is to get an application running and execute multiple tests on the same instance of the app.
I have tried this solution without much luck. Here is my test:
class ApplicationSpec extends Specification { sequential
object AppWithTestDb2 extends FakeApplication(additionalConfiguration =
Map(
("db.default.driver") -> "org.h2.Driver",
("db.default.url") -> (
// "jdbc:h2:mem:play-test-" + scala.util.Random.nextInt + // in memory database
"jdbc:h2:/tmp/play-test-" + scala.util.Random.nextInt + // file based db that can be accessed using h2-browse in activator
";MODE=PostgreSQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1")
))
running(AppWithTestDb2) {
"Application" should {
"send 404 on a bad request" in {
route(FakeRequest(GET, "/boum")) must beNone
}
"post signup request" in {
val res = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
status(res) must equalTo(OK)
contentType(res) must beSome("application/json")
contentAsJson(res) mustEqual TestData.newUser
}
"fail signup request for existing user" in {
val res1 = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
Await.result(res1, Duration.Inf)
val res = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
Await.result(res, Duration.Inf)
status(res) must equalTo(CONFLICT)
contentType(res) must beSome("application/json")
contentAsJson(res) mustEqual TestData.newUser
}
}
}
}
The problem here is that application starts and stops immediately and tests are executed without a running application:
[debug] c.j.b.BoneCPDataSource - JDBC URL = jdbc:h2:/tmp/play-test--437407884;MODE=PostgreSQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1, Username = zalando, partitions = 1, max (per partition) = 30, min (per partition) = 5, idle max age = 10 min, idle test period = 1 min, strategy = DEFAULT
[info] application - Application has started
[debug] application - Binding to Slick DAO implementations.
[info] application - Application shutdown...
[debug] c.j.b.BoneCPDataSource - Connection pool has been shut down
[info] ApplicationSpec
[info]
[info] Application should
[info] ! send 404 on a bad request
[error] RuntimeException: : There is no started application (Play.scala:71)
[error] play.api.Play$$anonfun$current$1.apply(Play.scala:71)
[error] play.api.Play$$anonfun$current$1.apply(Play.scala:71)
[error] play.api.Play$.current(Play.scala:71)
[error] play.api.test.RouteInvokers$class.route(Helpers.scala:305)
[error] play.api.test.Helpers$.route(Helpers.scala:403)
[error] ApplicationSpec$$anonfun$1$$anonfun$apply$7$$anonfun$apply$8$$anonfun$apply$9.apply(ApplicationSpec.scala:76)
[error] ApplicationSpec$$anonfun$1$$anonfun$apply$7$$anonfun$apply$8$$anonfun$apply$9.apply(ApplicationSpec.scala:76)
[error] ApplicationSpec$$anonfun$1$$anonfun$apply$7$$anonfun$apply$8.apply(ApplicationSpec.scala:76)
[error] ApplicationSpec$$anonfun$1$$anonfun$apply$7$$anonfun$apply$8.apply(ApplicationSpec.scala:76)

Here is my working solution
object AppWithTestDb2 extends FakeApplication(additionalConfiguration =
Map(
("db.default.driver") -> "org.h2.Driver",
("db.default.url") -> (
// "jdbc:h2:mem:play-test-" + scala.util.Random.nextInt + // in memory database
"jdbc:h2:/tmp/play-test-" + scala.util.Random.nextInt + // file based db that can be accessed using h2-browse in activator
";MODE=PostgreSQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1")
))
class SignupLoginSpec extends Specification { sequential
step(Play.start(AppWithTestDb2))
"application" should {
"post signup request" in {
val res = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
status(res) must equalTo(OK)
contentType(res) must beSome("application/json")
contentAsJson(res) mustEqual TestData.newUser
}
"fail signup request for existing user" in {
val res1 = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
Await.result(res1, Duration.Inf)
val res = route(FakeRequest(POST, "/user", FakeHeaders(), TestData.newUser)).get
Await.result(res, Duration.Inf)
status(res) must equalTo(CONFLICT)
contentType(res) must beSome("application/json")
contentAsJson(res) mustEqual TestData.newUser
}
}
step(Play.stop())
}

In specs2 there is a distinction between test declaration and test execution. When you write "application" should ... you just declare tests. The executable part is what is enclosed in the ... in ... part.
So when you declare running(AppWithTestDb2) { ... } you just create some tests inside the context of an AppTestDb2 application.
The general solution for what you want to achieve in specs2 is to use Steps like this:
class ApplicationSpec extends Specification { sequential
step("start application")
"Application" should {
"send 404 on a bad request" in { ... }
"post signup request" in { ... }
}
step("stop application")
}
Then, the way the specs2 execution model works, you will get your fake application started before all the tests start and terminated when all the tests are finished (whether you use sequential or not)
I am not a Play user but I suspect that you should be able to reuse the WithApplication class or something similar to create your start/stop steps. Otherwise there is a blog post here exploring a solution for the same problem.

Related

How do I test Play REST API with Json BodyParser?

I would like to write a simple test for a controller, which accepts a json body. But as soon I add the parse.json BodyParser to the Action my Tests cannot be compiled anymore.
The Setup is basically the plain play-scala-seed project.
Error:
[error] ... could not find implicit value for parameter mat: akka.stream.Materializer
[error] status(home) mustBe OK
[error] ^
HomeController:
def index() = Action { implicit request =>
Ok
}
def json() = Action(parse.json) { implicit request =>
Ok
}
HomeControllerSpec:
class HomeControllerSpec extends PlaySpec with GuiceOneAppPerTest with Injecting {
"HomeController POST" should {
"answer Ok" in {
val controller = new HomeController(stubControllerComponents())
val home = controller.json().apply(FakeRequest(POST, "/"))
status(home) mustBe OK
}
}
There seem to be two issues with the code in question regarding stubControllerComponents and FakeRequest calls.
Helpers.stubControllerComponents by default constructs ControllerComponents with NoMaterializer which simply throws an exception when used, so we need to provide an actual materializer as follows:
implicit val materializer = ActorMaterializer()(ActorSystem())
Helpers.stubControllerComponents(
playBodyParsers = Helpers.stubPlayBodyParsers(materializer)
)
The second issue is with FakeRequest where we need to provide a body as follows (otherwise we get 4xx error) :
FakeRequest(POST, "/json").withBody(Json.obj("name" -> "Jon Doe"))
Taking into account the above we can write the complete test as follows:
class HomeControllerSpec extends PlaySpec with GuiceOneAppPerTest {
"HomeController POST" should {
"answer Ok" in {
implicit val materializer = ActorMaterializer()(ActorSystem())
val controllerComponents =
Helpers.stubControllerComponents(
playBodyParsers = Helpers.stubPlayBodyParsers(materializer)
)
val controller = new HomeController(controllerComponents)
val fakeRequest =
FakeRequest(POST, "/json").withBody(Json.obj("name" -> "Jon Doe"))
val home = call(controller.json(), fakeRequest)
status(home) mustBe OK
}
}
}
It seems that you need to inject a materializer in your controller:
class Controller #Inject() (implicit val mat: Materializer) { ??? }

Scala Grpc failed with error io.grpc.StatusRuntimeException: CANCELLED: Failed to read message

I am trying to write a streaming service in Scala using GRPC. Towards this I wrote this proto file
syntax = "proto3";
package com.abhi.grpc;
message TimeRequest{}
message TimeResponse {
int64 currentTime = 1;
}
service Clock {
rpc StreamTime(TimeRequest) returns (stream TimeResponse);
}
This is my server side code
import com.abhi.grpc.clock.{ClockGrpc, TimeRequest, TimeResponse}
import io.grpc.stub.StreamObserver
import monix.execution.Scheduler
import monix.execution.Scheduler.{global => scheduler}
import scala.concurrent.duration._
object ClockGrpcServer extends GrpcServer with App {
val ssd = ClockGrpc.bindService(new ClockGRPC(), Scheduler.global)
runServer(ssd, "Clock")
}
class ClockGRPC extends ClockGrpc.Clock {
override def streamTime(request: TimeRequest, responseObserver: StreamObserver[TimeResponse]): Unit = {
scheduler.scheduleWithFixedDelay(0.seconds, 3.seconds) {
responseObserver.onNext(TimeResponse(System.currentTimeMillis))
}
}
}
and this is my client
object ClockGrpcClient extends App {
val channel = ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext(true).build()
val stub = ClockGrpc.stub(channel)
val observer = new StreamObserver[TimeResponse] {
override def onError(t: Throwable): Unit = println(s"failed with error ${t}")
override def onCompleted(): Unit = println("closing observer")
override def onNext(value: TimeResponse): Unit = println(s"received time ${new DateTime(value)}")
}
stub.streamTime(TimeRequest(), observer)
StdIn.readLine()
}
When I run the server and the client. The server throws the following error as soon as it receives any message from the client
io.grpc.StatusRuntimeException: CANCELLED
at io.grpc.Status.asRuntimeException(Status.java:534)
at io.grpc.stub.ServerCalls$ServerCallStreamObserverImpl.onNext(ServerCalls.java:279)
at com.abhi.ClockGRPC.$anonfun$streamTime$1(ClockGRPC.scala:22)
at monix.execution.internal.RunnableAction.run(RunnableAction.scala:25)
at monix.execution.schedulers.ReferenceScheduler$$anon$1.run(ReferenceScheduler.scala:45)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:140)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
I googled a little and found this article
https://blog.codecentric.de/en/2017/01/hello-grpc-scalapb/
based on this I changed my server to use the java.util scheduler
class ClockGRPC extends ClockGrpc.Clock {
val scheduler = Executors.newSingleThreadScheduledExecutor()
override def streamTime(request: TimeRequest, responseObserver: StreamObserver[TimeResponse]): Unit = {
val tick = new Runnable {
val counter = new AtomicInteger(10)
def run() =
if (counter.getAndDecrement() >= 0) {
val currentTime = System.currentTimeMillis()
responseObserver.onNext(TimeResponse(currentTime))
} else {
scheduler.shutdown()
responseObserver.onCompleted()
}
}
scheduler.scheduleAtFixedRate(tick, 0l, 3000l, TimeUnit.SECONDS)
}
}
But I still get the CANCELLED error. So i cannot get the streaming example to work.
I had almost given up on this problem. But came back today and resolved it.
The problem is with the line
override def onNext(value: TimeResponse): Unit = println(s"received time ${new DateTime(value)}")
value cannot be passed to new DateTime
To further make the matters worse. if the exception occurs in the callback method. Grpc Swallows it and replaces it with a generic error message
info] Running com.abhi.ClockGrpcClient failed with error io.grpc.StatusRuntimeException: CANCELLED: Failed to read message.
My bad luck that he DateTime uses an object as a parameter so the compilation succeeded, but the call failed at runtime and the exception was swallowed by the Grpc.
I am leaving this here so that it helps someone else.
[info] Running com.abhi.ClockGrpcClient failed with error io.grpc.StatusRuntimeException: CANCELLED: Failed to read message
Means the someone went wrong in the callback function.

Can I use play framework with akka http(not the one in play) and disable the netty server?

I'm not meaning the way play disabling PlayNettyServer and enable AkkaHttpServer which is described in the documentation by using:
lazy val root = (project in file("."))
.enablePlugins(PlayScala, PlayAkkaHttpServer)
.disablePlugins(PlayNettyServer)
I'm meaning that taking advantage of play framework's dependency injection and other tools like play-slick, and use akka-http directly in code like:
class AppInitiation #Inject()(implicit val system: ActorSystem, configuration: Configuration) {
implicit val materializer = ActorMaterializer()
implicit val timeout: Timeout = 5 seconds
val logger = Logger("Server")
val milkywayPath = path(Segment ~ RestPath)
val methods = get | put | post | delete
val gatewayExceptionHandler = ExceptionHandler {
case e: AskTimeoutException =>
complete(HttpResponse(InternalServerError, Nil, "Ask timeout!"))
case e: Exception =>
logger.error("unknown error", e)
complete(HttpResponse(InternalServerError, Nil, "Unknown error! Please contact administratro!"))
}
implicit def rejectionHandler = RejectionHandler.newBuilder()
.handle { case MissingHeaderRejection("X-Caller-Service") =>
complete(HttpResponse(BadRequest, Nil, "Missing required header X-Caller-Service!"))
}
.handle { case MissingQueryParamRejection("v") =>
complete(HttpResponse(BadRequest, Nil, "Missing required parameter v!"))
}
.result()
val requestHandler = system.actorOf(Props(new RequestHandler))
val routes = handleExceptions(gatewayExceptionHandler) {
(milkywayPath & methods & parameter('v.as[String]) & headerValueByName("X-Caller-Service") & extractRequest) {
(contextPath: String, resource: Path, v, caller, request) =>
complete {
val startTime = System.currentTimeMillis()
val callerInfo = CallerInfo(caller, contextPath, v, resource.toString())
val f = (requestHandler ? RequestHandlerMsg(callerInfo, request)).mapTo[HttpResponse]
f onComplete {
case r => accessLogger.info(s"method=${request.method.name} " +
s"uri=${request.uri} " +
s"caller=$caller " +
s"totalTime=${System.currentTimeMillis() - startTime}ms " +
s"resultStatus=${r.map(_.status.intValue).getOrElse(500)}")
}
f
}
}
}
val host = configuration.getString("gateway.host").getOrElse("localhost")
val port = configuration.getInt("gateway.port").getOrElse(9001)
Http().bindAndHandle(routes, host, port).onComplete(_ => logger.info(s"Server started listening request on port $port"))
}
and in module I can set it as:
override def configure(): Unit = {
bind(classOf[AppInitiation]).asEagerSingleton()
}
This works. But I still want to know how I can start it without PlayNettyServer running. I've tried:
lazy val `root` = (project in file("."))
.enablePlugins(PlayScala)
.disablePlugins(PlayNettyServer)
However there emerges the exception:
[info] p.a.l.c.ActorSystemProvider - Starting application default Akka system: application
play.core.server.ServerStartException: No ServerProvider configured with key 'play.server.provider'
at play.core.server.ServerProvider$$anonfun$1.apply(ServerProvider.scala:54)
at play.core.server.ServerProvider$$anonfun$1.apply(ServerProvider.scala:54)
I'm wondering if there is a way to take full advantage of Play framework with all its features and also build a higher performance server with akka http.

How to test file upload in Play 2.2.2?

I'm using Play 2.2.2 and have been unsuccessfully trying to add tests to my file upload functionality. I know there are plenty of questions about this, I have checked many but either they don't work or the API is deprecated and doesn't work or it fails with some spurious error.
This is how a valid request looks like from the browser (using Firefox's WebDeveloper plugin):
-----------------------------479326525221683770414613115
Content-Disposition: form-data; name="files[]"; filename="upload_aum_sample.csv"
Content-Type: text/csv
AccountName,AuM,Name
IX_CH1,10,A
IX_CH2,20,B
IX_CH3,30,C
IX_CH4,40,D
IX_CH5,50,E
IX_CH6,60,F
IX_CH7,70,G
IX_CH8,80,H
-----------------------------479326525221683770414613115--
This is what I have tried so far without success:
Compiles with a warning i.e. that routeAndCall is deprecated:
"upload file correctly" in new WithApplication {
val fileName = "upload_aum_sample.csv"
val file = getClass().getResource(fileName).getFile()
val data = MultipartFormData(Map(), List(FilePart("files[]", fileName, Some("text/csv"), file)), List(), List())
val result = routeAndCall(FakeRequest(POST, "/aum/upload/do", FakeHeaders(), data).withSession("username" -> "Test")).get
status(result) must equalTo(OK)
contentType(result) must beSome.which(_ == "application/json")
}
and results in the Exception:
[info] ! upload file correctly
[error] MatchError: <function1> (of class play.core.Router$Routes$$anon$4) (Helpers.scala:187)
[error] play.api.test.RouteInvokers$$anonfun$routeAndCall$1.apply(Helpers.scala:187)
[error] play.api.test.RouteInvokers$$anonfun$routeAndCall$1.apply(Helpers.scala:187)
[error] play.api.test.RouteInvokers$class.routeAndCall(Helpers.scala:187)
[error] AumUploadPageSpec.routeAndCall(AumUploadPageSpec.scala:30)
[error] play.api.test.RouteInvokers$class.routeAndCall(Helpers.scala:178)
[error] AumUploadPageSpec.routeAndCall(AumUploadPageSpec.scala:30)
[error] AumUploadPageSpec$$anonfun$12$$anon$3$delayedInit$body.apply(AumUploadPageSpec.scala:73)
[error] play.api.test.WithApplication$$anonfun$around$1.apply(Specs.scala:20)
[error] play.api.test.WithApplication$$anonfun$around$1.apply(Specs.scala:20)
[error] play.api.test.PlayRunners$class.running(Helpers.scala:45)
[error] play.api.test.Helpers$.running(Helpers.scala:364)
[error] play.api.test.WithApplication.around(Specs.scala:20)
[error] play.api.test.WithApplication.delayedInit(Specs.scala:17)
[error] AumUploadPageSpec$$anonfun$12$$anon$3.<init>(AumUploadPageSpec.scala:48)
[error] AumUploadPageSpec$$anonfun$12.apply(AumUploadPageSpec.scala:48)
[error] AumUploadPageSpec$$anonfun$12.apply(AumUploadPageSpec.scala:48)
Compiles fine no warnings
"upload file correctly" in new WithApplication {
val fileName = "upload_aum_sample.csv"
val file = getClass().getResource(fileName).getFile()
val data = MultipartFormData(Map(), List(FilePart("files[]", fileName, Some("text/csv"), file)), List(), List())
val result = controllers.Application.uploadDo("aum")(FakeRequest(POST, "/aum/upload/do", FakeHeaders(), data).withSession("username" -> "Test")).run
status(result) must equalTo(OK) // <<<<<<< test fails here
contentType(result) must beSome.which(_ == "application/json")
}
But the test fails due to the server responding 400 instead of 200 i.e. not OK
[info] x upload file correctly
[error] '400' is not equal to '200' (AumUploadPageSpec.scala:53)
UPDATE 1: If embed the file content rather than the file I still get the same errors i.e.
Change
val file = getClass().getResource(fileName).getFile()
to
val file = scala.io.Source.fromFile(getClass().getResource(fileName).getFile()).map(_.toByte).toArray
UPDATE 2: These are the routing and server side code respectively:
# Generic controllers
POST /:context/upload/do controllers.Application.uploadDo(context: String)
//------------------------------------------------------------------------
/**
* Action that uploads a file for a given context
* #param context the input context
*/
def uploadDo(context: String) = Action(parse.multipartFormData) { implicit request ⇒
request.body.file("files[]").map { file ⇒
val filename = file.filename
val contentType = file.contentType
}
Ok(Json.parse(
"""{"files": [
{
"name": "picture1.jpg",
"size": 902604,
"error": "Filetype not allowed"
},
{
"name": "picture2.jpg",
"size": 841946,
"error": "Filetype not allowed"
}
]}"""))
}
The top answer (irritatingly unaccepted) to this question solves my problem. I'm including it here for completeness.
trait FakeMultipartUpload {
case class WrappedFakeRequest[A](fr: FakeRequest[A]) {
def withMultipart(parts: (String, ContentBody)*) = {
// create a multipart form
val entity = new MultipartEntity()
parts.foreach { part =>
entity.addPart(part._1, part._2)
}
// serialize the form
val outputStream = new ByteArrayOutputStream
entity.writeTo(outputStream)
val bytes = outputStream.toByteArray
// inject the form into our request
val headerContentType = entity.getContentType.getValue
fr.withBody(bytes).withHeaders(CONTENT_TYPE -> headerContentType)
}
def withFileUpload(fileParam: String, file: File, contentType: String) = {
withMultipart(fileParam -> new FileBody(file, contentType))
}
}
implicit def toWrappedFakeRequest[A](fr: FakeRequest[A]) = WrappedFakeRequest(fr)
// override Play's equivalent Writeable so that the content-type header from the FakeRequest is used instead of application/octet-stream
implicit val wBytes: Writeable[Array[Byte]] = Writeable(identity, None)
}
#RunWith(classOf[JUnitRunner])
class AumUploadPageSpec extends PlaySpecification with FakeMultipartUpload {
//------------------------------------------------------------------------
"upload file correctly" in new WithApplication {
val fileName = "idxsrs_aum_2014-06-04.csv"
val uploadFile = new File(getClass().getResource(fileName).getPath())
val request = FakeRequest(POST, "/aum/upload/do").withFileUpload("files[]", uploadFile, "text/csv")
val response = route(request).get
status(response) must equalTo(OK)
contentType(response) must beSome.which(_ == "application/json")
}
}

Modularising scenarios to run in sequence using Gatling

I'm trying to modularise a series of performance tests in Gatling.
Several of the tests execute the same initial path through the pages, so I thought that I could break them down into a series of scenarios, each scenario being a series of shared actions defined in its own file, and then a final Simulation definition that simply executed the specified scenarios one after the other.
What I then need is for my Simulation to run those scenarios in sequence; but I can only find how to run them either concurrently, or with a specified delay between each. Is there any Simulation setup option to run the defined scenarios one after the other without specifying an arbitrary delay?
EDIT
Currently, I have the following set of files:
homepageHeaders.scala
package advanced
object homepageHeaders {
val homepage_headers_1 = Map(
"Accept" -> """text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8""",
"If-Modified-Since" -> """Wed, 20 Mar 2013 15:36:31 +0000""",
"If-None-Match" -> """"1363793791""""
)
}
homepageChain.scala
package advanced
import com.excilys.ebi.gatling.core.Predef._
import com.excilys.ebi.gatling.http.Predef._
import com.excilys.ebi.gatling.jdbc.Predef._
import akka.util.duration._
import homepageHeaders._
object homepageChain {
val homepageChain =
//Homepage
exec(http("homepage")
.get("/")
.headers(homepageHeaders.homepage_headers_1)
)
}
pageHeaders.scala
package advanced
object pageHeaders {
val page_headers_1 = Map(
"Accept" -> """text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"""
)
}
pageChain.scala
package advanced
import com.excilys.ebi.gatling.core.Predef._
import com.excilys.ebi.gatling.http.Predef._
import com.excilys.ebi.gatling.jdbc.Predef._
import akka.util.duration._
import pageHeaders._
object pageChain {
val pageChain =
//Page Menu
exec(http("page request")
.get("/page1")
.headers(pageHeaders.page_headers_1)
)
}
pageSimulation.scala
package advanced
import com.excilys.ebi.gatling.core.Predef._
import com.excilys.ebi.gatling.http.Predef._
import com.excilys.ebi.gatling.jdbc.Predef._
import homepageChain._
import pageChain._
class pageSimulation extends Simulation {
val urlBase = "http://www.mytestsite.com"
val httpConf = httpConfig
.baseURL(urlBase)
.acceptHeader("image/png,image/*;q=0.8,*/*;q=0.5")
.acceptEncodingHeader("gzip, deflate")
.acceptLanguageHeader("en-gb,en;q=0.5")
.userAgentHeader("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0")
val pageScenario = scenario("Bodycare Scenario")
.exec(homepageChain.homepageChain)
.exec(pageChain.pageChain)
setUp(
homepageScenario.users(1).protocolConfig(httpConf)
)
}
The error that I'm getting is:
14:40:50.800 [ERROR] c.e.e.g.a.ZincCompiler$ - /Gatling/user-files/simulations/advanced/pageChain.scala:13: not found: value exec
14:40:50.807 [ERROR] c.e.e.g.a.ZincCompiler$ - exec(http("page request")
14:40:50.808 [ERROR] c.e.e.g.a.ZincCompiler$ - ^
14:40:53.988 [ERROR] c.e.e.g.a.ZincCompiler$ - /Gatling/user-files/simulations/advanced/homepageChain.scala:13: not found: value exec
14:40:53.989 [ERROR] c.e.e.g.a.ZincCompiler$ - exec(http("homepage")
14:40:53.989 [ERROR] c.e.e.g.a.ZincCompiler$ - ^
14:41:17.274 [ERROR] c.e.e.g.a.ZincCompiler$ - two errors found
Exception in thread "main" Compilation failed
Clearly I'm missing something in my definition, but I just don't understand what it is
You can compose chains, not scenarios.
For example:
val login = exec(...)...
val foo = exec(...)...
val bar = exec(...)...
val scn1 = scenario("Scenario1").exec(login).exec(foo)
val scn2 = scenario("Scenario2").exec(login).exec(bar)
Clear?
You can cascade scenarios so that they execute in sequence as follows:
val allScenarios = scenario1.exec(scenario2).exec(scenario3)
Another option can be like this:
object GetAllRunDetails {
val getAllRunDetails = exec( ....)
}
object GetRunIdDetails{
val getRunIdDetails = exec( .... )
}
val scn1 = scenario("Scenario 1")
.exec(GetAllRunDetails.getAllRunDetails)
val scn2 = scenario("Scenario 2")
.exec(GetRunIdDetails.getRunIdDetails)
setUp(scn1.inject(atOnceUsers(1)),
scn2.inject(atOnceUsers(1)));
Thanks to Stephane, he also have given me a solution to create an object of multiple Chains and pass it to a scenario.
val login = exec(...)...
val foo = exec(...)...
val bar = exec(...)...
val scn_inpute = Seq(login, foo, bar)
val scn1 = scenario("Scenario1").exec(scn_inpute)
Since Gatling 3.4 Scenarios in the same simulation can now be executed sequentially with andThen.
setUp(
parent.inject(injectionProfile)
// child1 and child2 will start at the same time when last parent user will terminate
.andThen(
child1.inject(injectionProfile)
// grandChild will start when last child1 user will terminate
.andThen(grandChild.inject(injectionProfile)),
child2.inject(injectionProfile)
)
)
See official documentation.