RoundTripTester
Module
"io.kevinlee" %% "extras-hedgehog-circe" % "0.46.1" % Test
or for Scala.js
:
"io.kevinlee" %%% "extras-hedgehog-circe" % "0.46.1" % Test
Test JSON Encoder and Decoder
RoundTripTester
tests the available Circe JSON Encoder
and Decoder
by:
- Encoding the given type
A
into a JSONString
usingEncoder[A]
. - Decoding the JSON
String
back into the original typeA
usingDecoder[A]
.
RoundTripTester(sometypeA).test()
// hedgehog.Result
Examples:
import extras.hedgehog.circe.RoundTripTester
import cats._
import io.circe._
import hedgehog._
import hedgehog.runner._
object SomethingSpec extends Properties {
override def tests: List[Test] = List(
property("round-trip test Something JSON Codec", roundTripTest),
property("round-trip test Something JSON Codec - failure case", roundTripTestWithDecodeFailure),
property("round-trip test Something JSON Codec - failure case (indent 8)", roundTripTestWithDecodeFailureIndent8),
)
def roundTripTest: Property =
for {
something <- genSomething.log("something")
} yield {
RoundTripTester(something).test()
}
def roundTripTestWithDecodeFailure: Property =
for {
something <- genSomething
.map(something => SomethingWithDecodeFailure(something.id, something.name))
.log("something")
} yield {
RoundTripTester(something).test()
}
def roundTripTestWithDecodeFailureIndent8: Property =
for {
something <- genSomething
.map(something => SomethingWithDecodeFailure(something.id, something.name))
.log("something")
} yield {
RoundTripTester(something)
.indent(8)
.test()
}
def genSomething: Gen[Something] =
for {
id <- Gen.int(Range.linear(1, 100))
name <- Gen.string(Gen.unicode, Range.linear(3, 20))
} yield Something(id, name)
final case class Something(id: Int, name: String)
object Something {
implicit val somethingShow: Show[Something] =
something => s"Something(id = ${something.id.toString}, name = ${something.name})"
implicit val somethingCodec: Codec[Something] = io.circe.generic.semiauto.deriveCodec
}
final case class SomethingWithDecodeFailure(id: Int, name: String)
object SomethingWithDecodeFailure {
implicit val somethingWithDecodeFailureShow: Show[SomethingWithDecodeFailure] =
somethingWithDecodeFailure =>
List(
s"id = ${somethingWithDecodeFailure.id.toString}",
s"name = ${somethingWithDecodeFailure.name}",
).mkString(
"SomethingWithDecodeFailure(",
", ",
")",
)
implicit val somethingWithDecodeFailureEncoder: Encoder[SomethingWithDecodeFailure] =
io.circe.generic.semiauto.deriveEncoder
implicit val somethingWithDecodeFailureDecoder: Decoder[SomethingWithDecodeFailure] =
Decoder.instance(c =>
for {
id <- c.downField("blah").as[Int]
name <- c.downField("name").as[String]
} yield SomethingWithDecodeFailure(id, name)
)
}
}
// This is only for this document so you don't need this.
SomethingSpec.main(Array.empty)
// Using random seed: 100833991189
// [32m+[0m repl.MdocSession$MdocApp0$SomethingSpec$.round-trip test Something JSON Codec: OK, passed 100 tests
// [31m-[0m repl.MdocSession$MdocApp0$SomethingSpec$.round-trip test Something JSON Codec - failure case: Falsified after 0 passed tests
// > something: SomethingWithDecodeFailure(1,���)
// > Round-trip test for SomethingSpec.SomethingWithDecodeFailure failed with error:
// > Error: DecodingFailure at .blah: Missing required field
// > ---
// > Input: SomethingWithDecodeFailure(id = 1, name = ���)
// > ---
// > JSON: {
// "id" : 1,
// "name" : "\u0000\u0000\u0000"
// }
// >
// [31m-[0m repl.MdocSession$MdocApp0$SomethingSpec$.round-trip test Something JSON Codec - failure case (indent 8): Falsified after 0 passed tests
// > something: SomethingWithDecodeFailure(1,���)
// > Round-trip test for SomethingSpec.SomethingWithDecodeFailure failed with error:
// > Error: DecodingFailure at .blah: Missing required field
// > ---
// > Input: SomethingWithDecodeFailure(id = 1, name = ���)
// > ---
// > JSON: {
// "id" : 1,
// "name" : "\u0000\u0000\u0000"
// }
// >