Skip to content

Commit

Permalink
Migrate tests to MUnit (circe#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix Bruckmeier committed Dec 20, 2020
1 parent 465966e commit ddab8fc
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 144 deletions.
13 changes: 8 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ val compilerOptions = Seq(
"-language:higherKinds",
"-unchecked",
"-Ywarn-dead-code",
"-Ywarn-numeric-widen"
"-Ywarn-numeric-widen",
"-Yrangepos"
)

val circeVersion = "0.13.0"
val paradiseVersion = "2.1.1"

val jawnVersion = "1.0.0"
val scalaTestVersion = "3.2.3"
val scalaTestPlusVersion = "3.2.2.0"
val munitVersion = "0.7.20"
val disciplineMunitVersion = "1.0.4"

val previousCirceGenericExtrasVersion = "0.12.2"

Expand Down Expand Up @@ -84,10 +85,12 @@ lazy val genericExtras = crossProject(JSPlatform, JVMPlatform)
"io.circe" %%% "circe-generic" % circeVersion,
"io.circe" %%% "circe-literal" % circeVersion % Test,
"io.circe" %%% "circe-testing" % circeVersion % Test,
"org.scalatest" %%% "scalatest" % scalaTestVersion % Test,
"org.scalatestplus" %%% "scalacheck-1-14" % scalaTestPlusVersion % Test,
"org.scalameta" %%% "munit" % munitVersion % Test,
"org.scalameta" %%% "munit-scalacheck" % munitVersion % Test,
"org.typelevel" %% "discipline-munit" % disciplineMunitVersion % Test,
"org.typelevel" %% "jawn-parser" % jawnVersion % Test
),
testFrameworks := List(new TestFramework("munit.Framework")), // Override setting so Scalatest is disabled
ghpagesNoJekyll := true,
docMappingsApiDir := "api",
addMappingsToSiteDir(mappings in (Compile, packageDoc), docMappingsApiDir)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ package io.circe.generic.extras
import cats.instances._
import cats.syntax._
import io.circe.testing.{ ArbitraryInstances, EqInstances }
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatestplus.scalacheck.{ Checkers, ScalaCheckDrivenPropertyChecks }
import org.typelevel.discipline.Laws

import scala.language.implicitConversions
import munit.{ DisciplineSuite, Location, ScalaCheckSuite }

/**
* An opinionated stack of traits to improve consistency and reduce boilerplate in circe tests.
*/
trait CirceSuite
extends AnyFlatSpec
with ScalaCheckDrivenPropertyChecks
extends DisciplineSuite
with AllInstances
with AllInstancesBinCompat0
with AllInstancesBinCompat1
Expand All @@ -33,13 +32,5 @@ trait CirceSuite
with ArbitraryInstances
with EqInstances {

override def convertToEqualizer[T](left: T): Equalizer[T] =
sys.error("Intentionally ambiguous implicit for Equalizer")

implicit def prioritizedCatsSyntaxEither[A, B](eab: Either[A, B]): EitherOps[A, B] = new EitherOps(eab)

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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.circe.literal._
import io.circe.testing.CodecTests
import org.scalacheck.{ Arbitrary, Gen }
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Prop.forAll

import examples._

Expand Down Expand Up @@ -49,43 +50,46 @@ class ConfiguredAutoDerivedSuite extends CirceSuite {

{
implicit val config: Configuration = Configuration.default
checkLaws("Codec[ConfigExampleBase] (default configuration)", CodecTests[ConfigExampleBase].codec)
checkAll("Codec[ConfigExampleBase] (default configuration)", CodecTests[ConfigExampleBase].codec)
}

"Configuration#transformMemberNames" should "support member name transformation using snake_case" in forAll {
foo: ConfigExampleFoo =>
property("Configuration#transformMemberNames should support member name transformation using snake_case") {
forAll { foo: ConfigExampleFoo =>
implicit val snakeCaseConfig: Configuration = Configuration.default.withSnakeCaseMemberNames

import foo._
val json = json"""{ "this_is_a_field": $thisIsAField, "a": $a, "b": $b}"""

assert(Encoder[ConfigExampleFoo].apply(foo) === json)
assert(Decoder[ConfigExampleFoo].decodeJson(json) === Right(foo))
}
}

"Configuration#transformMemberNames" should "support member name transformation using SCREAMING_SNAKE_CASE" in forAll {
foo: ConfigExampleFoo =>
property("Configuration#transformMemberNames should support member name transformation using SCREAMING_SNAKE_CASE") {
forAll { foo: ConfigExampleFoo =>
implicit val snakeCaseConfig: Configuration = Configuration.default.withScreamingSnakeCaseMemberNames

import foo._
val json = json"""{ "THIS_IS_A_FIELD": $thisIsAField, "A": $a, "B": $b}"""

assert(Encoder[ConfigExampleFoo].apply(foo) === json)
assert(Decoder[ConfigExampleFoo].decodeJson(json) === Right(foo))
}
}

"Configuration#transformMemberNames" should "support member name transformation using kebab-case" in forAll {
foo: ConfigExampleFoo =>
property("Configuration#transformMemberNames should support member name transformation using kebab-case") {
forAll { foo: ConfigExampleFoo =>
implicit val kebabCaseConfig: Configuration = Configuration.default.withKebabCaseMemberNames

import foo._
val json = json"""{ "this-is-a-field": $thisIsAField, "a": $a, "b": $b}"""

assert(Encoder[ConfigExampleFoo].apply(foo) === json)
assert(Decoder[ConfigExampleFoo].decodeJson(json) === Right(foo))
}
}

"Configuration#useDefaults" should "support using default values during decoding" in {
property("Configuration#useDefaults should support using default values during decoding") {
forAll { (f: String, b: Double) =>
implicit val withDefaultsConfig: Configuration = Configuration.default.withDefaults

Expand All @@ -111,34 +115,34 @@ class ConfiguredAutoDerivedSuite extends CirceSuite {

implicit val customConfig: Configuration = Configuration.default.withDefaults

"Option[T] without default" should "be None if null decoded" in {
test("Option[T] without default should be None if null decoded") {
val json = json"""{ "a": null }"""
assert(Decoder[FooNoDefault].decodeJson(json) === Right(FooNoDefault(None, "b")))
}

"Option[T] without default" should "be None if missing key decoded" in {
test("Option[T] without default should be None if missing key decoded") {
val json = json"""{}"""
assert(Decoder[FooNoDefault].decodeJson(json) === Right(FooNoDefault(None, "b")))
}

"Option[T] with default" should "be None if null decoded" in {
test("Option[T] with default should be None if null decoded") {
val json = json"""{ "a": null }"""
assert(Decoder[FooWithDefault].decodeJson(json) === Right(FooWithDefault(None, "b")))
}

"Option[T] with default" should "be default value if missing key decoded" in {
test("Option[T] with default should be default value if missing key decoded") {
val json = json"""{}"""
assert(Decoder[FooWithDefault].decodeJson(json) === Right(FooWithDefault(Some(0), "b")))
assert(Decoder[FooWithDefault].decodeAccumulating(json.hcursor) === Validated.valid(FooWithDefault(Some(0), "b")))
}

"Value with default" should "be default value if value is null" in {
test("Value with default should be default value if value is null") {
val json = json"""{"b": null}"""
assert(Decoder[FooWithDefault].decodeJson(json) === Right(FooWithDefault(Some(0), "b")))
assert(Decoder[FooWithDefault].decodeAccumulating(json.hcursor) === Validated.valid(FooWithDefault(Some(0), "b")))
}

"Option[T] with default" should "fail to decode if type in json is not correct" in {
test("Option[T] with default should fail to decode if type in json is not correct") {
val json = json"""{"a": "NotAnInt"}"""
assert(Decoder[FooWithDefault].decodeJson(json) === Left(DecodingFailure("Int", List(DownField("a")))))
assert(
Expand All @@ -147,7 +151,7 @@ class ConfiguredAutoDerivedSuite extends CirceSuite {
)
}

"Field with default" should "fail to decode it type in json is not correct" in {
test("Field with default should fail to decode it type in json is not correct") {
val json = json"""{"b": 1}"""
assert(Decoder[FooWithDefault].decodeJson(json) === Left(DecodingFailure("String", List(DownField("b")))))
assert(
Expand All @@ -158,7 +162,7 @@ class ConfiguredAutoDerivedSuite extends CirceSuite {
}
}

"Configuration#discriminator" should "support a field indicating constructor" in {
property("Configuration#discriminator should support a field indicating constructor") {
forAll { foo: ConfigExampleFoo =>
implicit val withDefaultsConfig: Configuration = Configuration.default.withDiscriminator("type")

Expand All @@ -170,8 +174,8 @@ class ConfiguredAutoDerivedSuite extends CirceSuite {
}
}

"Configuration#transformConstructorNames" should "support constructor name transformation with snake_case" in forAll {
foo: ConfigExampleFoo =>
property("Configuration#transformConstructorNames should support constructor name transformation with snake_case") {
forAll { foo: ConfigExampleFoo =>
implicit val snakeCaseConfig: Configuration =
Configuration.default.withDiscriminator("type").withSnakeCaseConstructorNames

Expand All @@ -180,10 +184,13 @@ class ConfiguredAutoDerivedSuite extends CirceSuite {

assert(Encoder[ConfigExampleBase].apply(foo) === json)
assert(Decoder[ConfigExampleBase].decodeJson(json) === Right(foo))
}
}

"Configuration#transformConstructorNames" should "support constructor name transformation with SCREAMING_SNAKE_CASE" in forAll {
foo: ConfigExampleFoo =>
property(
"Configuration#transformConstructorNames should support constructor name transformation with SCREAMING_SNAKE_CASE"
) {
forAll { foo: ConfigExampleFoo =>
implicit val snakeCaseConfig: Configuration =
Configuration.default.withDiscriminator("type").withScreamingSnakeCaseConstructorNames

Expand All @@ -192,10 +199,11 @@ class ConfiguredAutoDerivedSuite extends CirceSuite {

assert(Encoder[ConfigExampleBase].apply(foo) === json)
assert(Decoder[ConfigExampleBase].decodeJson(json) === Right(foo))
}
}

"Configuration#transformConstructorNames" should "support constructor name transformation with kebab-case" in forAll {
foo: ConfigExampleFoo =>
property("Configuration#transformConstructorNames should support constructor name transformation with kebab-case") {
forAll { foo: ConfigExampleFoo =>
implicit val kebabCaseConfig: Configuration =
Configuration.default.withDiscriminator("type").withKebabCaseConstructorNames

Expand All @@ -204,29 +212,32 @@ class ConfiguredAutoDerivedSuite extends CirceSuite {

assert(Encoder[ConfigExampleBase].apply(foo) === json)
assert(Decoder[ConfigExampleBase].decodeJson(json) === Right(foo))
}
}

"Configuration options" should "work together" in forAll { (f: String, b: Double) =>
implicit val customConfig: Configuration =
Configuration.default.withSnakeCaseMemberNames.withDefaults.withDiscriminator("type")
property("Configuration options should work together") {
forAll { (f: String, b: Double) =>
implicit val customConfig: Configuration =
Configuration.default.withSnakeCaseMemberNames.withDefaults.withDiscriminator("type")

val foo: ConfigExampleBase = ConfigExampleFoo(f, 0, b)
val json = json"""{ "type": "ConfigExampleFoo", "this_is_a_field": $f, "b": $b}"""
val expected = json"""{ "type": "ConfigExampleFoo", "this_is_a_field": $f, "a": 0, "b": $b}"""
val foo: ConfigExampleBase = ConfigExampleFoo(f, 0, b)
val json = json"""{ "type": "ConfigExampleFoo", "this_is_a_field": $f, "b": $b}"""
val expected = json"""{ "type": "ConfigExampleFoo", "this_is_a_field": $f, "a": 0, "b": $b}"""

assert(Encoder[ConfigExampleBase].apply(foo) === expected)
assert(Decoder[ConfigExampleBase].decodeJson(json) === Right(foo))
assert(Encoder[ConfigExampleBase].apply(foo) === expected)
assert(Decoder[ConfigExampleBase].decodeJson(json) === Right(foo))
}
}

{
import defaults._
checkLaws("Codec[Tuple1[Int]]", CodecTests[Tuple1[Int]].codec)
checkLaws("Codec[(Int, Int, Foo)]", CodecTests[(Int, Int, Foo)].codec)
checkLaws("Codec[Qux[Int]]", CodecTests[Qux[Int]].codec)
checkLaws("Codec[Foo]", CodecTests[Foo].codec)
checkAll("Codec[Tuple1[Int]]", CodecTests[Tuple1[Int]].codec)
checkAll("Codec[(Int, Int, Foo)]", CodecTests[(Int, Int, Foo)].codec)
checkAll("Codec[Qux[Int]]", CodecTests[Qux[Int]].codec)
checkAll("Codec[Foo]", CodecTests[Foo].codec)

"Decoder[Int => Qux[String]]" should "decode partial JSON representations" in forAll {
(i: Int, s: String, j: Int) =>
property("Decoder[Int => Qux[String]] should decode partial JSON representations") {
forAll { (i: Int, s: String, j: Int) =>
val result = Json
.obj(
"a" -> Json.fromString(s),
Expand All @@ -236,6 +247,7 @@ class ConfiguredAutoDerivedSuite extends CirceSuite {
.map(_(i))

assert(result === Right(Qux(i, s, j)))
}
}
}
}
Loading

0 comments on commit ddab8fc

Please sign in to comment.