Skip to content

Commit f482052

Browse files
authored
Refactor Play EnumFormatsSpec to be more DRY and cover more code paths (#48)
* Refactor Play EnumFormatsSpec to be more DRY and cover more code paths * Refactor BSONHandlerSpec for DRYness and coverage * Use handler as writer * Refactor PlayEnumSpec for DRYness and coverage * Fix bug found in PlayUppercaseQueryBindableEnum
1 parent b7f45b8 commit f482052

File tree

8 files changed

+410
-265
lines changed

8 files changed

+410
-265
lines changed

enumeratum-play-json/src/test/scala/enumeratum/Dummy.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ sealed trait Dummy extends EnumEntry
77
object Dummy extends Enum[Dummy] with PlayJsonEnum[Dummy] {
88
case object A extends Dummy
99
case object B extends Dummy
10-
case object C extends Dummy
10+
case object c extends Dummy
1111
val values = findValues
1212
}

enumeratum-play-json/src/test/scala/enumeratum/EnumFormatsSpec.scala

+120-91
Original file line numberDiff line numberDiff line change
@@ -2,113 +2,142 @@ package enumeratum
22

33
import org.scalatest.OptionValues._
44
import org.scalatest.{ FunSpec, Matchers }
5-
import play.api.libs.json.{ JsNumber, JsResult, JsString }
5+
import play.api.libs.json._
66

77
class EnumFormatsSpec extends FunSpec with Matchers {
88

9-
describe("reads") {
10-
val reads = EnumFormats.reads(Dummy)
11-
12-
it("should create a reads that works with valid values") {
13-
reads.reads(JsString("A")).asOpt.value should be(Dummy.A)
14-
}
15-
16-
it("should create a reads that fails with invalid values") {
17-
reads.reads(JsString("D")).isError should be(true)
18-
errorMessages(reads.reads(JsString("D"))) should be(Seq("error.expected.validenumvalue"))
19-
reads.reads(JsNumber(2)).isError should be(true)
20-
errorMessages(reads.reads(JsNumber(2))) should be(Seq("error.expected.enumstring"))
21-
}
22-
}
23-
24-
describe("reads insensitive") {
25-
val reads = EnumFormats.reads(Dummy, true)
26-
27-
it("should create a reads that works with valid values disregarding case") {
28-
reads.reads(JsString("A")).asOpt.value should be(Dummy.A)
29-
reads.reads(JsString("a")).asOpt.value should be(Dummy.A)
30-
}
9+
testScenario(
10+
descriptor = "normal operation",
11+
reads = EnumFormats.reads(Dummy),
12+
readSuccessExpectations = Map("A" -> Dummy.A),
13+
readErrors = Map("C" -> Seq("error.expected.validenumvalue")),
14+
writes = EnumFormats.writes(Dummy),
15+
writeExpectations = Map(Dummy.A -> "A"),
16+
formats = EnumFormats.formats(Dummy)
17+
)
18+
19+
testScenario(
20+
descriptor = "case insensitive",
21+
reads = EnumFormats.reads(enum = Dummy, insensitive = true),
22+
readSuccessExpectations = Map(
23+
"A" -> Dummy.A,
24+
"a" -> Dummy.A
25+
),
26+
readErrors = Map.empty,
27+
writes = EnumFormats.writes(Dummy),
28+
writeExpectations = Map(Dummy.A -> "A"),
29+
formats = EnumFormats.formats(enum = Dummy, insensitive = true)
30+
)
31+
32+
testScenario(
33+
descriptor = "lower case transformed",
34+
reads = EnumFormats.readsLowercaseOnly(Dummy),
35+
readSuccessExpectations = Map(
36+
"a" -> Dummy.A
37+
),
38+
readErrors = Map(
39+
"A" -> Seq("error.expected.validenumvalue")
40+
),
41+
writes = EnumFormats.writesLowercaseOnly(Dummy),
42+
writeExpectations = Map(Dummy.A -> "a"),
43+
formats = EnumFormats.formatsLowerCaseOnly(Dummy)
44+
)
45+
46+
testScenario(
47+
descriptor = "upper case transformed",
48+
reads = EnumFormats.readsUppercaseOnly(Dummy),
49+
readSuccessExpectations = Map(
50+
"A" -> Dummy.A,
51+
"C" -> Dummy.c
52+
),
53+
readErrors = Map(
54+
"a" -> Seq("error.expected.validenumvalue")
55+
),
56+
writes = EnumFormats.writesUppercaseOnly(Dummy),
57+
writeExpectations = Map(Dummy.A -> "A"),
58+
formats = EnumFormats.formatsUppercaseOnly(Dummy)
59+
)
60+
61+
// Bunch of shared testing methods
62+
63+
private def errorMessages(jsResult: JsResult[_]): Seq[String] =
64+
jsResult.fold(
65+
_.collect {
66+
case (path, errors) => errors.map(_.message).mkString
67+
},
68+
_ => Seq.empty
69+
)
3170

32-
it("should create a reads that fails with invalid values") {
33-
reads.reads(JsString("D")).isError should be(true)
34-
errorMessages(reads.reads(JsString("D"))) should be(Seq("error.expected.validenumvalue"))
35-
reads.reads(JsNumber(2)).isError should be(true)
36-
errorMessages(reads.reads(JsNumber(2))) should be(Seq("error.expected.enumstring"))
37-
}
71+
private def testScenario(
72+
descriptor: String,
73+
reads: Reads[Dummy],
74+
readSuccessExpectations: Map[String, Dummy],
75+
readErrors: Map[String, Seq[String]],
76+
writes: Writes[Dummy],
77+
writeExpectations: Map[Dummy, String],
78+
formats: Format[Dummy]
79+
): Unit = describe(descriptor) {
80+
testReads(reads, readSuccessExpectations, readErrors)
81+
testWrites(writes, writeExpectations)
82+
testFormats(formats, readSuccessExpectations, readErrors, writeExpectations)
3883
}
3984

40-
describe("reads lower case") {
41-
val reads = EnumFormats.readsLowercaseOnly(Dummy)
42-
43-
it("should create a reads that works with valid values that are lower case") {
44-
reads.reads(JsString("a")).asOpt.value should be(Dummy.A)
45-
}
46-
47-
it("should create a reads that fails with invalid values") {
48-
reads.reads(JsString("A")).isError should be(true)
49-
errorMessages(reads.reads(JsString("A"))) should be(Seq("error.expected.validenumvalue"))
85+
/**
86+
* Shared scenarios for testing Reads
87+
*/
88+
private def testReads(
89+
reads: Reads[Dummy],
90+
expectedSuccesses: Map[String, Dummy],
91+
expectedErrors: Map[String, Seq[String]]
92+
): Unit = describe("Reads") {
93+
val expectedFails: Map[JsValue, Seq[String]] = {
94+
val withJsValueKeys = expectedErrors.map { case (k, v) => JsString(k) -> v }
95+
// Add standard errors
96+
withJsValueKeys ++ Map(
97+
JsNumber(2) -> Seq("error.expected.enumstring"),
98+
JsString("D") -> Seq("error.expected.validenumvalue")
99+
)
50100
}
51-
}
52-
53-
describe("reads upper case") {
54-
val reads = EnumFormats.readsUppercaseOnly(Dummy)
55101

56-
it("should create a reads that works with valid values that are lower case") {
57-
reads.reads(JsString("A")).asOpt.value should be(Dummy.A)
102+
it("should create a reads that works with valid values") {
103+
expectedSuccesses.foreach {
104+
case (name, expected) =>
105+
reads.reads(JsString(name)).asOpt.value should be(expected)
106+
}
58107
}
59108

60109
it("should create a reads that fails with invalid values") {
61-
reads.reads(JsString("a")).isError should be(true)
62-
errorMessages(reads.reads(JsString("a"))) should be(Seq("error.expected.validenumvalue"))
110+
expectedFails.foreach {
111+
case (k, v) =>
112+
val result = reads.reads(k)
113+
result.isError shouldBe true
114+
errorMessages(result) shouldBe v
115+
}
63116
}
64117
}
65118

66-
describe("writes") {
67-
val writer = EnumFormats.writes(Dummy)
68-
119+
/**
120+
* Shared scenarios for testing Writes
121+
*/
122+
private def testWrites(writer: Writes[Dummy], expectations: Map[Dummy, String]): Unit = describe("Writes") {
69123
it("should create a writes that writes enum values to JsString") {
70-
writer.writes(Dummy.A) should be(JsString("A"))
71-
}
72-
}
73-
74-
describe("writes lower case") {
75-
val writer = EnumFormats.writesLowercaseOnly(Dummy)
76-
77-
it("should create a writes that writes enum values to JsString as lower case") {
78-
writer.writes(Dummy.A) should be(JsString("a"))
124+
expectations.foreach {
125+
case (k, v) =>
126+
writer.writes(k) should be(JsString(v))
127+
}
79128
}
80129
}
81130

82-
describe("writes upper case") {
83-
val writer = EnumFormats.writesUppercaseOnly(Dummy)
84-
85-
it("should create a writes that writes enum values to JsString as upper case") {
86-
writer.writes(Dummy.A) should be(JsString("A"))
87-
}
131+
/**
132+
* Shared scenarios for testing Formats
133+
*/
134+
private def testFormats(
135+
formats: Format[Dummy],
136+
expectedReadSuccesses: Map[String, Dummy],
137+
expectedReadErrors: Map[String, Seq[String]],
138+
expectedWrites: Map[Dummy, String]
139+
): Unit = describe("Formats") {
140+
testReads(formats, expectedReadSuccesses, expectedReadErrors)
141+
testWrites(formats, expectedWrites)
88142
}
89-
90-
describe("formats") {
91-
val format = EnumFormats.formats(Dummy)
92-
93-
it("should create a format that works with valid values") {
94-
format.reads(JsString("A")).asOpt.value should be(Dummy.A)
95-
}
96-
97-
it("should create a format that fails with invalid values") {
98-
format.reads(JsString("D")).isError should be(true)
99-
format.reads(JsNumber(2)).isError should be(true)
100-
}
101-
102-
it("should create a format that writes enum values to JsString") {
103-
format.writes(Dummy.A) should be(JsString("A"))
104-
}
105-
}
106-
107-
def errorMessages(jsResult: JsResult[_]): Seq[String] =
108-
jsResult.fold(
109-
_.collect {
110-
case (path, errors) => errors.map(_.message).mkString
111-
},
112-
_ => Seq.empty
113-
)
114-
}
143+
}

enumeratum-play/src/main/scala/enumeratum/PlayUppercaseQueryBindableEnum.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ package enumeratum
33
import play.api.mvc.QueryStringBindable
44

55
trait PlayUppercaseQueryBindableEnum[A <: EnumEntry] { self: Enum[A] =>
6-
implicit val queryBindable: QueryStringBindable[A] = UrlBinders.queryBinderLowercaseOnly(self)
6+
implicit val queryBindable: QueryStringBindable[A] = UrlBinders.queryBinderUppercaseOnly(self)
77
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package enumeratum
2+
3+
/**
4+
* Created by Lloyd on 2/4/15.
5+
*/
6+
sealed trait PlayDummyNormal extends EnumEntry
7+
8+
object PlayDummyNormal extends PlayEnum[PlayDummyNormal] {
9+
case object A extends PlayDummyNormal
10+
case object B extends PlayDummyNormal
11+
case object c extends PlayDummyNormal
12+
val values = findValues
13+
}
14+
15+
sealed trait PlayDummyLowerOnly extends EnumEntry
16+
17+
object PlayDummyLowerOnly extends PlayLowercaseEnum[PlayDummyLowerOnly] {
18+
case object A extends PlayDummyLowerOnly
19+
case object B extends PlayDummyLowerOnly
20+
case object c extends PlayDummyLowerOnly
21+
val values = findValues
22+
}
23+
24+
sealed trait PlayDummyUpperOnly extends EnumEntry
25+
26+
object PlayDummyUpperOnly extends PlayUppercaseEnum[PlayDummyUpperOnly] {
27+
case object A extends PlayDummyUpperOnly
28+
case object B extends PlayDummyUpperOnly
29+
case object c extends PlayDummyUpperOnly
30+
val values = findValues
31+
}
32+

enumeratum-play/src/test/scala/enumeratum/PlayDummy.scala

-13
This file was deleted.

0 commit comments

Comments
 (0)