From c82056d5daac31d36741fa55e53db685312c7670 Mon Sep 17 00:00:00 2001 From: rcardin Date: Wed, 2 Oct 2024 09:21:11 +0200 Subject: [PATCH 1/4] feat(87): Added MUnit module --- build.sbt | 16 ++++++++++++++-- munit-raise4s/README.md | 8 ++++++++ .../in/rcard/raise4s/munit/RaiseSuite.scala | 7 +++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 munit-raise4s/README.md create mode 100644 munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseSuite.scala diff --git a/build.sbt b/build.sbt index 2eb9671..bf1be3f 100644 --- a/build.sbt +++ b/build.sbt @@ -40,7 +40,15 @@ lazy val `cats-raise4s` = project .settings( name := "cats-raise4s", scalaVersion := scala3Version, - libraryDependencies ++= commonDependencies ++ `cats-raised4sDependencies` + libraryDependencies ++= commonDependencies ++ `cats-raise4sDependencies` + ) + +lazy val `munit-raise4s` = project + .dependsOn(core) + .settings( + name := "munit-raise4s", + scalaVersion := scala3Version, + libraryDependencies ++= commonDependencies ++ `munit-raise4sDependencies` ) lazy val raise4s = (project in file(".")) @@ -59,6 +67,10 @@ lazy val commonDependencies = Seq( dependencies.scalatest % Test ) -lazy val `cats-raised4sDependencies` = Seq( +lazy val `cats-raise4sDependencies` = Seq( "org.typelevel" %% "cats-core" % "2.12.0" ) + +lazy val `munit-raise4sDependencies` = Seq( + "org.scalameta" %% "munit" % "1.0.2" +) diff --git a/munit-raise4s/README.md b/munit-raise4s/README.md new file mode 100644 index 0000000..daab945 --- /dev/null +++ b/munit-raise4s/README.md @@ -0,0 +1,8 @@ +![GitHub Workflow Status (with branch)](https://img.shields.io/github/actions/workflow/status/rcardin/raise4s/scala.yml?branch=main) +![Maven Central](https://img.shields.io/maven-central/v/in.rcard.raise4s/munit-raise4s_3) +[![javadoc](https://javadoc.io/badge2/in.rcard.raise4s/muint-raise4s_3/javadoc.svg)](https://javadoc.io/doc/in.rcard.raise4s/munit-raise4s_3) +
+ +# MUnit Integration for Raise4s + +TODO \ No newline at end of file diff --git a/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseSuite.scala b/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseSuite.scala new file mode 100644 index 0000000..ec6ed57 --- /dev/null +++ b/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseSuite.scala @@ -0,0 +1,7 @@ +package in.rcard.raise4s.munit + +import munit.FunSuite + +abstract class RaiseSuite extends FunSuite { + +} From 98dc3aa01ff5d40061eaa69b1f31677a67919aa6 Mon Sep 17 00:00:00 2001 From: rcardin Date: Wed, 18 Dec 2024 11:37:11 +0100 Subject: [PATCH 2/4] feat(87): Updated MUnit version to 1.0.3 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 81e222c..7a9059c 100644 --- a/build.sbt +++ b/build.sbt @@ -72,5 +72,5 @@ lazy val `cats-raise4sDependencies` = Seq( ) lazy val `munit-raise4sDependencies` = Seq( - "org.scalameta" %% "munit" % "1.0.2" + "org.scalameta" %% "munit" % "1.0.3" ) From e90fe08da5c1f4bc39a7a80da857831ff20f8536 Mon Sep 17 00:00:00 2001 From: rcardin Date: Thu, 19 Dec 2024 17:34:01 +0100 Subject: [PATCH 3/4] feat(87): Added a minimal set of function to handle raised errors in tests --- .../rcard/raise4s/munit/RaiseAssertions.scala | 43 +++++++++++++++ .../in/rcard/raise4s/munit/RaiseSuite.scala | 55 ++++++++++++++++++- .../src/test/scala/RaiseSuiteSpec.scala | 38 +++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseAssertions.scala create mode 100644 munit-raise4s/src/test/scala/RaiseSuiteSpec.scala diff --git a/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseAssertions.scala b/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseAssertions.scala new file mode 100644 index 0000000..b3ad0d4 --- /dev/null +++ b/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseAssertions.scala @@ -0,0 +1,43 @@ +package in.rcard.raise4s.munit + +import in.rcard.raise4s.Raise +import munit.Assertions.fail +import munit.Location + +import scala.reflect.ClassTag + +/** Set of assertions for tests that can raise errors. + */ +trait RaiseAssertions { + + /** Evaluates the given expression and asserts that the expected raised error is of type `T`. + * + *

Example

+ * {{{ + * val error: String = interceptR[String] { + * Raise.raise("An error occurred") + * } + * assertEquals(error, "An error occurred") + * }}} + * + * @param body + * The function that should raise an error + * @tparam Error + * The type of the error to intercept + * @return + * The raised error + */ + def interceptR[Error]( + body: Raise[Error] ?=> Any + )(implicit ErrorClass: ClassTag[Error], loc: Location): Error = { + val result: Error | Any = Raise.run { + body + } + result match + case error: Error => error + case _ => + val expectedExceptionMsg = + s"Expected error of type '${ErrorClass.runtimeClass.getName}' but body evaluated successfully" + fail(expectedExceptionMsg) + } +} diff --git a/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseSuite.scala b/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseSuite.scala index ec6ed57..0d77271 100644 --- a/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseSuite.scala +++ b/munit-raise4s/src/main/scala/in/rcard/raise4s/munit/RaiseSuite.scala @@ -1,7 +1,58 @@ package in.rcard.raise4s.munit -import munit.FunSuite +import in.rcard.raise4s.Raise +import munit.{FunSuite, Location, TestOptions} -abstract class RaiseSuite extends FunSuite { +/** Suite that provides a test methods that can handle computations that can raise errors. + */ +abstract class RaiseSuite extends FunSuite with RaiseAssertions { + /** A test for computations that can raise errors.

Example

+ * {{{ + * testR("A test should succeed on successful Raise instances") { + * val one: Int raises String = 1 + * val two: Int raises String = 2 + * assert(one < two) + * } + * }}} + * + * @tparam Error + * The type of the error that can be raised + */ + def testR[Error](name: String)(body: Raise[Error] ?=> Unit)(implicit loc: Location): Unit = { + test(new TestOptions(name))( + Raise.fold( + block = body, + recover = error => + fail(s"Expected the test not to raise any errors but it did with error '$error'"), + transform = identity + ) + ) + } + + /** A test for computations that can raise errors and that can be configured with the given + * options.

Example

+ * {{{ + * testR("A test should fail on failed Raise instances".fail) { + * val one: Int raises String = Raise.raise("An error occurred") + * val two: Int raises String = 2 + * assert(one < two) + * } + * }}} + * + * @tparam Error + * The type of the error that can be raised + */ + def testR[Error]( + options: TestOptions + )(body: Raise[Error] ?=> Unit)(implicit loc: Location): Unit = { + test(options)( + Raise.fold( + block = body, + recover = error => + fail(s"Expected the test not to raise any errors but it did with error '$error'"), + transform = identity + ) + ) + } } diff --git a/munit-raise4s/src/test/scala/RaiseSuiteSpec.scala b/munit-raise4s/src/test/scala/RaiseSuiteSpec.scala new file mode 100644 index 0000000..811a9fe --- /dev/null +++ b/munit-raise4s/src/test/scala/RaiseSuiteSpec.scala @@ -0,0 +1,38 @@ +import in.rcard.raise4s.munit.RaiseSuite +import in.rcard.raise4s.{Raise, raises} +import munit.FailException + +class RaiseSuiteSpec extends RaiseSuite { + testR("A test should succeed on successful Raise instances") { + val one: Int raises String = 1 + val two: Int raises String = 2 + assert(one < two) + } + + testR("A test should fail on failed Raise instances".fail) { + val one: Int raises String = Raise.raise("An error occurred") + val two: Int raises String = 2 + assert(one < two) + } + + testR("intercept assertion should succeed on a raised error") { + val error: String = interceptR[String] { + Raise.raise("An error occurred") + } + + assertEquals(error, "An error occurred") + } + + testR("intercept assertion should fail on a successful Raise instance") { + val actualException = intercept[FailException] { + interceptR[String] { + 42 + } + } + + assertEquals( + actualException.getMessage, + "Expected error of type 'java.lang.String' but body evaluated successfully" + ) + } +} From e15991ad4249396f6f7872f212459727d3652cae Mon Sep 17 00:00:00 2001 From: rcardin Date: Fri, 20 Dec 2024 11:47:03 +0100 Subject: [PATCH 4/4] feat(87): Added information to the README file --- munit-raise4s/README.md | 58 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/munit-raise4s/README.md b/munit-raise4s/README.md index daab945..cd64f3d 100644 --- a/munit-raise4s/README.md +++ b/munit-raise4s/README.md @@ -5,4 +5,60 @@ # MUnit Integration for Raise4s -TODO \ No newline at end of file +Integration of the Raise DSL with the MUnit testing framework. + +## Dependency + +The library is available on Maven Central. To use it, add the following dependency to your `build.sbt` files: + +```sbt +libraryDependencies += "in.rcard.raise4s" %% "munit-raise4s" % "0.4.0" +``` + +The library is only available for Scala 3. + +## Usage + +The library let you test functions that can raise errors with the `munit` testing framework. Please refer to the [mUnit documentation](https://scalameta.org/munit/) for more general information about the testing framework. + +To declare a new test, use the `testR` method available through the `RaiseSuite` test suite. The method accepts a body that can raise errors. For example: + +```scala 3 +import in.rcard.raise4s.munit.RaiseSuite + +class RaiseSuiteSpec extends RaiseSuite { + testR("A test should succeed on successful Raise instances") { + val one: Int raises String = 1 + val two: Int raises String = 2 + assert(one < two) + } +} +``` + +If the body raises an error, the test will fail with the error message. For example, the following test fails: + +```scala 3 +testR("A test should fail on failed Raise instances") { + val one: Int raises String = Raise.raise("An error occurred") + val two: Int raises String = 2 + assert(one < two) +} +``` + +The message we receive is: + +``` +munit.FailException: Expected the test not to raise any errors but it did with error 'An error occurred' +``` + +If you want to test if a block raises an error and you want to assert some properties on the error, you can use the `in.rcard.raise4s.munit.RaiseAssertions.interceptR` method: + +```scala 3 +testR("intercept assertion should succeed on a raised error") { + val error: String = interceptR[String] { + Raise.raise("An error occurred") + } + assertEquals(error, "An error occurred") +} +``` +