Skip to content

Commit

Permalink
Add munit test framework (#536)
Browse files Browse the repository at this point in the history
* Add munit test framework

* scalafmt

---------

Co-authored-by: PJ Fanning <pjfanning@users.noreply.github.com>
  • Loading branch information
Philippus and pjfanning committed Apr 23, 2024
1 parent 0c18a23 commit f8b0a22
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 0 deletions.
10 changes: 10 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ lazy val userProjects: Seq[ProjectReference] = List[ProjectReference](
httpCaching,
httpCors,
httpTestkit,
httpTestkitMunit,
httpMarshallersScala,
httpMarshallersJava,
httpSprayJson,
Expand Down Expand Up @@ -224,6 +225,15 @@ lazy val httpTestkit = project("http-testkit")
.enablePlugins(ReproducibleBuildsPlugin)
.disablePlugins(MimaPlugin) // testkit, no bin compat guaranteed

lazy val httpTestkitMunit = project("http-testkit-munit")
.settings(commonSettings)
.settings(AutomaticModuleName.settings("pekko.http.testkit.munit"))
.dependsOn(http, httpTestkit)
.addPekkoModuleDependency("pekko-stream-testkit", "provided", PekkoCoreDependency.default)
.addPekkoModuleDependency("pekko-testkit", "provided", PekkoCoreDependency.default)
.settings(Dependencies.httpTestkitMunit)
.disablePlugins(MimaPlugin) // testkit, no bin compat guaranteed

lazy val httpTests = project("http-tests")
.settings(commonSettings)
.settings(Dependencies.httpTests)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class HttpExt @InternalStableApi /* constructor signature is hardcoded in Teleme
"pekko-http",
"pekko-http-caching",
"pekko-http-testkit",
"pekko-http-testkit-munit",
"pekko-http-marshallers-scala",
"pekko-http-marshallers-java",
"pekko-http-spray-json",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.pekko.http.scaladsl.testkit.munit

import org.apache.pekko.http.scaladsl.testkit.RouteTest

trait MunitRouteTest extends MunitTestFramework with RouteTest
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.pekko.http.scaladsl.testkit.munit

import munit.FunSuite
import org.apache.pekko.http.scaladsl.server.ExceptionHandler
import org.apache.pekko.http.scaladsl.testkit.TestFrameworkInterface

trait MunitTestFramework extends FunSuite with TestFrameworkInterface {
override def failTest(msg: String): Nothing = throw new AssertionError(msg)

override def afterAll(): Unit = {
cleanUp()
super.afterAll()
}

override val testExceptionHandler: ExceptionHandler = ExceptionHandler {
case e: munit.ComparisonFailException => throw e
case e: munit.FailSuiteException => throw e
case e: munit.FailException => throw e
case e: java.lang.AssertionError => throw e
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.pekko.http.scaladsl.testkit.munit

import scala.concurrent.Await
import scala.concurrent.duration._

import org.apache.pekko
import pekko.http.scaladsl.model._
import HttpMethods._
import StatusCodes._
import pekko.http.scaladsl.server._
import Directives._
import munit.FailException
import pekko.http.scaladsl.model.headers.{ `X-Forwarded-Proto`, RawHeader }
import pekko.actor.ActorRef
import pekko.pattern.ask
import pekko.stream.scaladsl.Source
import pekko.testkit._
import pekko.util.{ ByteString, Timeout }

class MunitRouteTestSpec extends MunitRouteTest {
override def testConfigSource: String = "pekko.http.server.transparent-head-requests = on" // see test below

test("The MunitRouteTest should support the most simple and direct route test") {
Get() ~> complete(HttpResponse()) ~> { rr =>
rr.awaitResult
assertEquals(rr.response, HttpResponse())
}
}

test("The MunitRouteTest should support a test using a directive and some checks") {
val pinkHeader = RawHeader("Fancy", "pink")
Get() ~> addHeader(pinkHeader) ~> {
respondWithHeader(pinkHeader) {
complete("abc")
}
} ~> check {
assertEquals(status, OK)
assertEquals(responseEntity, HttpEntity(ContentTypes.`text/plain(UTF-8)`, "abc"))
assertEquals(header("Fancy"), Some(pinkHeader))
}
}

test("The MunitRouteTest should support a test using ~!> and some checks") {
// raw here, should have been parsed into modelled header when going through an actual server when using `~!>`
val extraHeader = RawHeader("X-Forwarded-Proto", "abc")
Get() ~!> {
respondWithHeader(extraHeader) {
complete("abc")
}
} ~> check {
assertEquals(status, OK)
assertEquals(responseEntity, HttpEntity(ContentTypes.`text/plain(UTF-8)`, "abc"))
assertEquals(header[`X-Forwarded-Proto`].get, `X-Forwarded-Proto`("abc"))
}
}

test("The MunitRouteTest should support test checking a route that returns infinite chunks") {
Get() ~> {
val infiniteSource =
Source.unfold(0L)(acc => Some((acc + 1, acc)))
.throttle(1, 20.millis)
.map(i => ByteString(i.toString))
complete(HttpEntity(ContentTypes.`application/octet-stream`, infiniteSource))
} ~> check {
assertEquals(status, OK)
assertEquals(contentType, ContentTypes.`application/octet-stream`)
val future = chunksStream.take(5).runFold(Vector.empty[Int])(_ :+ _.data.utf8String.toInt)
assertEquals(Await.result(future, 5.seconds), (0 until 5).toVector)
}
}

test("The MunitRouteTest should support proper rejection collection") {
Post("/abc", "content") ~> {
(get | put) {
complete("naah")
}
} ~> check {
assertEquals(rejections, List(MethodRejection(GET), MethodRejection(PUT)))
}
}

test("The MunitRouteTest should support separation of route execution from checking") {
val pinkHeader = RawHeader("Fancy", "pink")

case object Command
val service = TestProbe()
val handler = TestProbe()
implicit def serviceRef: ActorRef = service.ref
implicit val askTimeout: Timeout = 1.second.dilated

val result =
Get() ~> pinkHeader ~> {
respondWithHeader(pinkHeader) {
complete(handler.ref.ask(Command).mapTo[String])
}
} ~> runRoute

handler.expectMsg(Command)
handler.reply("abc")

check {
assertEquals(status, OK)
assertEquals(responseEntity, HttpEntity(ContentTypes.`text/plain(UTF-8)`, "abc"))
assertEquals(header("Fancy"), Some(pinkHeader))
}(result)
}

test("The MunitRouteTest should support failing the test inside the route") {
val route = get {
fail("BOOM")
}

intercept[FailException] {
Get() ~> route
}
}

test("The MunitRouteTest should support throwing an AssertionError inside the route") {
val route = get {
throw new AssertionError("test")
}

intercept[AssertionError] {
Get() ~> route
}
}

test("The MunitRouteTest should support internal server error") {
val route = get {
throw new RuntimeException("BOOM")
}

Get() ~> route ~> check {
assertEquals(status, InternalServerError)
}
}

test("The MunitRouteTest should fail if testing a HEAD request with ~> and `transparent-head-request = on`") {
def runTest(): Unit = Head() ~> complete("Ok") ~> check {}

interceptMessage[AssertionError](
"`pekko.http.server.transparent-head-requests = on` not supported in RouteTest using `~>`. " +
"Use `~!>` instead for a full-stack test, e.g. `req ~!> route ~> check {...}`") {
runTest()
}
}
}
4 changes: 4 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ object Dependencies {
val sprayJson = Compile.sprayJson % "test"
val junit = Compile.junit % "test"
val specs2 = "org.specs2" %% "specs2-core" % "4.20.3"
val munit = "org.scalameta" %% "munit" % "0.7.29"

val scalacheck = "org.scalacheck" %% "scalacheck" % scalaCheckVersion % "test"
val junitIntf = "com.github.sbt" % "junit-interface" % "0.13.3" % "test"
Expand Down Expand Up @@ -124,6 +125,9 @@ object Dependencies {
Test.junit, Test.junitIntf, Compile.junit % "provided",
Test.scalatest.withConfigurations(Some("provided; test"))))

lazy val httpTestkitMunit =
l ++= Seq(Test.munit % "provided; test")

lazy val httpTests = l ++= Seq(Test.junit, Test.scalatest, Test.junitIntf)

lazy val httpXml = Seq(
Expand Down

0 comments on commit f8b0a22

Please sign in to comment.