From 1c30d09ac3c73593b3b62c0b20ed1fbd64bce7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Zu=CC=88hlke?= Date: Sun, 12 Feb 2023 22:19:01 +0100 Subject: [PATCH 1/2] convert tests to munit * replaces scalatest with munit as the test framework * uses discipline-munit and munit-cats-effect-3 for better integration * the number of tests decreases because doc test for scalatest are split into smaller tests --- build.sbt | 18 ++--- .../io.circe.config/CirceConfigSpec.scala | 78 ++++++++++--------- .../io.circe.config/CirceConfigSpec.scala | 78 ++++++++++--------- .../io.circe.config/CirceConfigLaws.scala | 18 ++--- 4 files changed, 93 insertions(+), 99 deletions(-) diff --git a/build.sbt b/build.sbt index 52447c0..ad83048 100644 --- a/build.sbt +++ b/build.sbt @@ -26,13 +26,11 @@ ThisBuild / tlMimaPreviousVersions ++= Set( ) val Versions = new { - val catsEffect = "3.4.6" val circe = "0.14.4" val config = "1.4.2" - val discipline = "1.4.0" - val scalaCheck = "1.15.4" - val scalaTest = "3.2.11" - val scalaTestPlus = "3.2.11.0" + val munit = "0.7.29" + val disciplineMunit = "1.0.9" + val munitcatsEffect = "1.0.7" } lazy val root = tlCrossRootProject.aggregate(config) @@ -47,13 +45,11 @@ lazy val config = project "io.circe" %% "circe-parser" % Versions.circe, "io.circe" %% "circe-generic" % Versions.circe % Test, "io.circe" %% "circe-testing" % Versions.circe % Test, - "org.typelevel" %% "cats-effect" % Versions.catsEffect % Test, - "org.typelevel" %% "discipline-core" % Versions.discipline % Test, - "org.scalacheck" %% "scalacheck" % Versions.scalaCheck % Test, - "org.scalatest" %% "scalatest" % Versions.scalaTest % Test, - "org.scalatestplus" %% "scalacheck-1-15" % Versions.scalaTestPlus % Test + "org.scalameta" %% "munit" % Versions.munit % Test, + "org.typelevel" %% "discipline-munit" % Versions.disciplineMunit % Test, + "org.typelevel" %% "munit-cats-effect-3" % Versions.munitcatsEffect % Test ), - doctestTestFramework := DoctestTestFramework.ScalaTest, + doctestTestFramework := DoctestTestFramework.Munit, doctestMarkdownEnabled := true, tlVersionIntroduced := Map( "2.12" -> "0.3.0", diff --git a/config/src/test/scala-2/io.circe.config/CirceConfigSpec.scala b/config/src/test/scala-2/io.circe.config/CirceConfigSpec.scala index 4a5deaa..f07206f 100644 --- a/config/src/test/scala-2/io.circe.config/CirceConfigSpec.scala +++ b/config/src/test/scala-2/io.circe.config/CirceConfigSpec.scala @@ -25,89 +25,91 @@ package io.circe.config import cats.effect.IO -import cats.effect.unsafe.implicits.global import com.typesafe.config.{parser => _, _} import io.circe.config.syntax._ import io.circe.generic.auto._ import io.circe.{parser => _, _} -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers +import munit.CatsEffectSuite import java.time.Period import scala.concurrent.duration._ import scala.io.Source -class CirceConfigSpec extends AnyFlatSpec with Matchers { +class CirceConfigSpec extends CatsEffectSuite { import CirceConfigSpec._ - trait ParserTests { - def parse: Either[ParsingFailure, Json] - def decode: Either[Error, TestConfig] + def testParser(parse: Either[ParsingFailure, Json], decode: Either[Error, TestConfig]): Unit = { assert(parse.isRight) val Right(config) = decode - assert(config == DecodedTestConfig) - assert(config.k.getDouble("ka") == 1.1) - assert(config.k.getString("kb") == "abc") - assert(config.l.unwrapped == "localhost") + assertEquals(config, DecodedTestConfig) + assertEqualsDouble(config.k.getDouble("ka"), 1.1, 1e-6) + assertEquals(config.k.getString("kb"), "abc") + assertEquals(config.l.unwrapped, "localhost") } - "parser" should "parse and decode config from string" in new ParserTests { - def parse = parser.parse(AppConfigString) - def decode = parser.decode[TestConfig](AppConfigString) + test("parser should parse and decode config from string") { + testParser( + parser.parse(AppConfigString), + parser.decode[TestConfig](AppConfigString) + ) } - it should "parse and decode config from object" in new ParserTests { - def parse = parser.parse(AppConfig) - def decode = parser.decode[TestConfig](AppConfig) + test("parser should parse and decode config from object") { + testParser( + parser.parse(AppConfig), + parser.decode[TestConfig](AppConfig) + ) } - it should "parse and decode config from file" in new ParserTests { + test("parser should parse and decode config from file") { def file = resolveFile("CirceConfigSpec.conf") - def parse = parser.parseFile(file) - def decode = parser.decodeFile[TestConfig](file) + testParser( + parser.parseFile(file), + parser.decodeFile[TestConfig](file) + ) } - it should "parse and decode config from default typesafe config resolution" in { - parser.decode[AppSettings]().fold(fail(_), _ should equal(DecodedAppSettings)) + test("parser should parse and decode config from default typesafe config resolution") { + assertEquals(parser.decode[AppSettings](), Right(DecodedAppSettings)) } - it should "parse and decode config from default typesafe config resolution via ApplicativeError" in { - parser.decodeF[IO, AppSettings]().unsafeRunSync() should equal(DecodedAppSettings) + test("parser should parse and decode config from default typesafe config resolution via ApplicativeError") { + assertIO(parser.decodeF[IO, AppSettings](), DecodedAppSettings) } - it should "parse and decode config from default typesafe config resolution with path via ApplicativeError" in { - parser.decodePathF[IO, HttpSettings]("http").unsafeRunSync() should equal(DecodedAppSettings.http) + test("parser should parse and decode config from default typesafe config resolution with path via ApplicativeError") { + assertIO(parser.decodePathF[IO, HttpSettings]("http"), DecodedAppSettings.http) } - "printer" should "print it into a config string" in { + test("printer should print it into a config string") { val Right(json) = parser.parse(AppConfig) val expected = readFile("CirceConfigSpec.printed.conf") - assert(printer.print(json) == expected) + assertEquals(printer.print(json), expected) } - "syntax" should "provide Config decoder" in { - assert(AppConfig.as[TestConfig] == Right(DecodedTestConfig)) + test("syntax should provide Config decoder") { + assertEquals(AppConfig.as[TestConfig], Right(DecodedTestConfig)) } - it should "provide syntax to decode at a given path" in { - assert(AppConfig.as[Nested]("e") == Right(Nested(true))) + test("syntax should provide syntax to decode at a given path") { + assertEquals(AppConfig.as[Nested]("e"), Right(Nested(true))) } - it should "provide Config decoder via ApplicativeError" in { - assert(AppConfig.asF[IO, TestConfig].unsafeRunSync() == DecodedTestConfig) + test("syntax should provide Config decoder via ApplicativeError") { + assertIO(AppConfig.asF[IO, TestConfig], DecodedTestConfig) } - it should "provide syntax to decode at a given path via ApplicativeError" in { - assert(AppConfig.asF[IO, Nested]("e").unsafeRunSync() == Nested(true)) + test("syntax should provide syntax to decode at a given path via ApplicativeError") { + assertIO(AppConfig.asF[IO, Nested]("e"), Nested(true)) } - "round-trip" should "parse and print" in { + test("round-trip should parse and print") { for (file <- testResourcesDir.listFiles) { val Right(json) = parser.parseFile(file) - assert(parser.parse(printer.print(json)) == Right(json), s"round-trip failed for ${file.getName}") + assertEquals(parser.parse(printer.print(json)), Right(json), s"round-trip failed for ${file.getName}") } } } diff --git a/config/src/test/scala-3/io.circe.config/CirceConfigSpec.scala b/config/src/test/scala-3/io.circe.config/CirceConfigSpec.scala index eeca941..5f7965f 100644 --- a/config/src/test/scala-3/io.circe.config/CirceConfigSpec.scala +++ b/config/src/test/scala-3/io.circe.config/CirceConfigSpec.scala @@ -25,89 +25,91 @@ package io.circe.config import cats.effect.IO -import cats.effect.unsafe.implicits.global import com.typesafe.config.{parser => _, _} import io.circe.config.syntax._ import io.circe.syntax._ import io.circe.{parser => _, _} -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers +import munit.CatsEffectSuite import java.time.Period import scala.concurrent.duration._ import scala.io.Source -class CirceConfigSpec extends AnyFlatSpec with Matchers { +class CirceConfigSpec extends CatsEffectSuite { import CirceConfigSpec._ - trait ParserTests { - def parse: Either[ParsingFailure, Json] - def decode: Either[Error, TestConfig] + def testParser(parse: Either[ParsingFailure, Json], decode: Either[Error, TestConfig]): Unit = { assert(parse.isRight) val Right(config) = decode - assert(config == DecodedTestConfig) - assert(config.k.getDouble("ka") == 1.1) - assert(config.k.getString("kb") == "abc") - assert(config.l.unwrapped == "localhost") + assertEquals(config, DecodedTestConfig) + assertEqualsDouble(config.k.getDouble("ka"), 1.1, 1e-6) + assertEquals(config.k.getString("kb"), "abc") + assertEquals(config.l.unwrapped, "localhost") } - "parser" should "parse and decode config from string" in new ParserTests { - def parse = parser.parse(AppConfigString) - def decode = parser.decode[TestConfig](AppConfigString) + test("parser should parse and decode config from string") { + testParser( + parser.parse(AppConfigString), + parser.decode[TestConfig](AppConfigString) + ) } - it should "parse and decode config from object" in new ParserTests { - def parse = parser.parse(AppConfig) - def decode = parser.decode[TestConfig](AppConfig) + test("parser should parse and decode config from object") { + testParser( + parser.parse(AppConfig), + parser.decode[TestConfig](AppConfig) + ) } - it should "parse and decode config from file" in new ParserTests { + test("parser should parse and decode config from file") { def file = resolveFile("CirceConfigSpec.conf") - def parse = parser.parseFile(file) - def decode = parser.decodeFile[TestConfig](file) + testParser( + parser.parseFile(file), + parser.decodeFile[TestConfig](file) + ) } - it should "parse and decode config from default typesafe config resolution" in { - parser.decode[AppSettings]().fold(fail(_), _ should equal(DecodedAppSettings)) + test("parser should parse and decode config from default typesafe config resolution") { + assertEquals(parser.decode[AppSettings](), Right(DecodedAppSettings)) } - it should "parse and decode config from default typesafe config resolution via ApplicativeError" in { - parser.decodeF[IO, AppSettings]().unsafeRunSync() should equal(DecodedAppSettings) + test("parser should parse and decode config from default typesafe config resolution via ApplicativeError") { + assertIO(parser.decodeF[IO, AppSettings](), DecodedAppSettings) } - it should "parse and decode config from default typesafe config resolution with path via ApplicativeError" in { - parser.decodePathF[IO, HttpSettings]("http").unsafeRunSync() should equal(DecodedAppSettings.http) + test("parser should parse and decode config from default typesafe config resolution with path via ApplicativeError") { + assertIO(parser.decodePathF[IO, HttpSettings]("http"), DecodedAppSettings.http) } - "printer" should "print it into a config string" in { + test("printer should print it into a config string") { val Right(json) = parser.parse(AppConfig) val expected = readFile("CirceConfigSpec.printed.conf") - assert(printer.print(json) == expected) + assertEquals(printer.print(json), expected) } - "syntax" should "provide Config decoder" in { - assert(AppConfig.as[TestConfig] == Right(DecodedTestConfig)) + test("syntax should provide Config decoder") { + assertEquals(AppConfig.as[TestConfig], Right(DecodedTestConfig)) } - it should "provide syntax to decode at a given path" in { - assert(AppConfig.as[Nested]("e") == Right(Nested(true))) + test("syntax should provide syntax to decode at a given path") { + assertEquals(AppConfig.as[Nested]("e"), Right(Nested(true))) } - it should "provide Config decoder via ApplicativeError" in { - assert(AppConfig.asF[IO, TestConfig].unsafeRunSync() == DecodedTestConfig) + test("syntax should provide Config decoder via ApplicativeError") { + assertIO(AppConfig.asF[IO, TestConfig], DecodedTestConfig) } - it should "provide syntax to decode at a given path via ApplicativeError" in { - assert(AppConfig.asF[IO, Nested]("e").unsafeRunSync() == Nested(true)) + test("syntax should provide syntax to decode at a given path via ApplicativeError") { + assertIO(AppConfig.asF[IO, Nested]("e"), Nested(true)) } - "round-trip" should "parse and print" in { + test("round-trip should parse and print") { for (file <- testResourcesDir.listFiles) { val Right(json) = parser.parseFile(file) - assert(parser.parse(printer.print(json)) == Right(json), s"round-trip failed for ${file.getName}") + assertEquals(parser.parse(printer.print(json)), Right(json), s"round-trip failed for ${file.getName}") } } } diff --git a/config/src/test/scala/io.circe.config/CirceConfigLaws.scala b/config/src/test/scala/io.circe.config/CirceConfigLaws.scala index 237f104..fbdde46 100644 --- a/config/src/test/scala/io.circe.config/CirceConfigLaws.scala +++ b/config/src/test/scala/io.circe.config/CirceConfigLaws.scala @@ -33,13 +33,12 @@ import io.circe.Json import io.circe.Parser import io.circe.ParsingFailure import io.circe.testing.ParserTests +import munit.DisciplineSuite import org.scalacheck.Arbitrary import org.scalacheck.Prop -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatestplus.scalacheck.Checkers import org.typelevel.discipline.Laws -class CirceConfigLaws extends AnyFlatSpec { +class CirceConfigLaws extends DisciplineSuite { implicit val arbitraryConfigJson: Arbitrary[Json] = Arbitrary { def normalize(json: Json): Json = @@ -67,13 +66,8 @@ class CirceConfigLaws extends AnyFlatSpec { yield normalize(Json.fromJsonObject(jsonObject)) } - def checkLaws(name: String, ruleSet: Laws#RuleSet): Unit = ruleSet.all.properties.zipWithIndex.foreach { - case ((id, prop), 0) => name should s"obey $id" in Checkers.check(prop) - case ((id, prop), _) => it should s"obey $id" in Checkers.check(prop) - } - - checkLaws("Parser", ParserTests(parser).fromString) - checkLaws( + checkAll("Parser", ParserTests(parser).fromString) + checkAll( "Parser", ParserTests(parser).fromFunction[Config]("fromConfig")( ConfigFactory.parseString, @@ -82,8 +76,8 @@ class CirceConfigLaws extends AnyFlatSpec { _.decodeAccumulating[Json] ) ) - checkLaws("Printer", PrinterTests(parser).fromJson) - checkLaws("Codec", CodecTests[Config](syntax.configDecoder, parser.parse).fromFunction("fromConfig")) + checkAll("Printer", PrinterTests(parser).fromJson) + checkAll("Codec", CodecTests[Config](syntax.configDecoder, parser.parse).fromFunction("fromConfig")) } case class PrinterTests(parser: Parser) extends Laws { From 38894ead74056833bb97a978987bb4963d517019 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Mon, 13 Feb 2023 08:27:29 +0100 Subject: [PATCH 2/2] Rename version val --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index ad83048..bcfea56 100644 --- a/build.sbt +++ b/build.sbt @@ -30,7 +30,7 @@ val Versions = new { val config = "1.4.2" val munit = "0.7.29" val disciplineMunit = "1.0.9" - val munitcatsEffect = "1.0.7" + val munitCatsEffect = "1.0.7" } lazy val root = tlCrossRootProject.aggregate(config) @@ -47,7 +47,7 @@ lazy val config = project "io.circe" %% "circe-testing" % Versions.circe % Test, "org.scalameta" %% "munit" % Versions.munit % Test, "org.typelevel" %% "discipline-munit" % Versions.disciplineMunit % Test, - "org.typelevel" %% "munit-cats-effect-3" % Versions.munitcatsEffect % Test + "org.typelevel" %% "munit-cats-effect-3" % Versions.munitCatsEffect % Test ), doctestTestFramework := DoctestTestFramework.Munit, doctestMarkdownEnabled := true,