Last active
December 4, 2017 17:29
-
-
Save DamianReeves/2994e9f05a74af97f0a66ba440af388b to your computer and use it in GitHub Desktop.
Shapeless Expectations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package experiments | |
import experiments.expectation.EquivalenceExpectationResult | |
import shapeless._ | |
trait EquivalenceExpectation[-In] { | |
type Result | |
def apply(actual:In, expected:In):Result | |
} | |
trait EquivalenceExpectation0 { | |
implicit def generic[F,G]( | |
implicit | |
gen: Generic.Aux[F,G], | |
genExpect: Lazy[EquivalenceExpectation[G]] | |
): EquivalenceExpectation.Aux[F, genExpect.value.Result] = new EquivalenceExpectation[F] { | |
type Result = genExpect.value.Result | |
def apply(actual:F, expected:F):Result = genExpect.value.apply( | |
gen.to(actual), | |
gen.to(expected) | |
) | |
} | |
} | |
object EquivalenceExpectation extends EquivalenceExpectation0 { | |
type Expectation[In] = EquivalenceExpectation[In] | |
type ExpectationResult[In] = EquivalenceExpectationResult[In] | |
import expectation.{Equivalent, NotEquivalent} | |
def apply[In](implicit expect: Lazy[Expectation[In]]): Aux[In, expect.value.Result] = expect.value | |
type Aux[In, Result0] = Expectation[In] {type Result = Result0} | |
implicit val booleanExpectation: EquivalenceExpectation.Aux[Boolean, ExpectationResult[Boolean]] = new Expectation[Boolean] { | |
type Result = ExpectationResult[Boolean] | |
def apply(actual: Boolean, expected: Boolean): Result = | |
if (actual == expected) Equivalent[Boolean](actual, expected) | |
else NotEquivalent[Boolean](actual, expected) | |
} | |
implicit val intExpectation: EquivalenceExpectation.Aux[Int, ExpectationResult[Int]] = new Expectation[Int] { | |
type Result = ExpectationResult[Int] | |
def apply(actual: Int, expected: Int): Result = | |
if (actual == expected) Equivalent[Int](actual, expected) | |
else NotEquivalent[Int](actual, expected) | |
} | |
implicit val stringExpectation: EquivalenceExpectation.Aux[String, ExpectationResult[String]] = new Expectation[String] { | |
type Result = ExpectationResult[String] | |
def apply(actual: String, expected: String): Result = | |
if (actual.equalsIgnoreCase(expected)) Equivalent[String](actual, expected) | |
else NotEquivalent[String](actual, expected) | |
} | |
implicit val bigDecimalExpectation: EquivalenceExpectation.Aux[BigDecimal, ExpectationResult[BigDecimal]] = new Expectation[BigDecimal] { | |
type Result = ExpectationResult[BigDecimal] | |
def apply(actual: BigDecimal, expected: BigDecimal): Result = | |
if (actual == expected) Equivalent[BigDecimal](actual, expected) | |
else NotEquivalent[BigDecimal](actual, expected) | |
} | |
implicit def optionExpectation[T]( | |
implicit expectT: Lazy[Expectation[T]] | |
): EquivalenceExpectation.Aux[Option[T], ExpectationResult[Option[T]]] = new Expectation[Option[T]] { | |
type Result = ExpectationResult[Option[T]] | |
def apply(actual: Option[T], expected: Option[T]): Result = (actual, expected) match { | |
case (None,None) => NotEquivalent(None,None) | |
case (Some(a),Some(e)) => expectT.value.apply(a,e) match { | |
case Equivalent(_, _) => Equivalent(actual, expected) | |
case NotEquivalent(_,_) => NotEquivalent(actual,expected) | |
} | |
case (Some(_), None) => NotEquivalent(actual, expected) | |
case (None, Some(_)) => NotEquivalent(actual, expected) | |
} | |
} | |
implicit def deriveHNil: EquivalenceExpectation.Aux[HNil, ExpectationResult[HNil]] = new Expectation[HNil] { | |
type Result = ExpectationResult[HNil] | |
def apply(actual: HNil, expected: HNil): ExpectationResult[HNil] = Equivalent(HNil, HNil) | |
} | |
implicit def derivedHCons[H, T <: HList]( | |
implicit | |
expectH: Expectation[H], | |
expectT: Lazy[Expectation[T] { type Result <:HList }] | |
):EquivalenceExpectation.Aux[H :: T, expectH.Result :: expectT.value.Result] = new Expectation[H :: T]{ | |
type Result = expectH.Result :: expectT.value.Result | |
def apply(actual:H :: T, expected: H :: T):Result = { | |
expectH(actual.head, expected.head) :: expectT.value(actual.tail, expected.tail) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package experiments | |
import org.scalatest.{FlatSpec, Matchers, WordSpec} | |
import expectation.ExpectationSyntax._ | |
import experiments.EquivalenceExpectationSpecs.Dog | |
import shapeless._ | |
class EquivalenceExpectationSpecs extends WordSpec with Matchers { | |
"A Boolean value" when { | |
"true" should { | |
"be equivalent to another true value" in { | |
val result = true.isExpectedToBeEquivalentTo(true) | |
result shouldBe expectation.Equivalent(true, true) | |
} | |
"not be equivalent to false" in { | |
var result = true.isExpectedToBeEquivalentTo(false) | |
result shouldBe expectation.NotEquivalent(true, false) | |
} | |
} | |
} | |
"An Int value" when { | |
"x" should { | |
"be equivalent to y when y = x" in { | |
val x,y = 100 | |
x.isExpectedToBeEquivalentTo(y) shouldBe expectation.Equivalent(x,y) | |
} | |
"NOT be equivalent to y when y != x" in { | |
val x = 100 | |
val y = 200 | |
x.isExpectedToBeEquivalentTo(y) shouldBe expectation.NotEquivalent(x,y) | |
} | |
} | |
} | |
"An Option value" when { | |
"Some(inner)" should { | |
"evaluate as equivalent wben the expectation is checked against the same value" in { | |
Option("Hello World").isExpectedToBeEquivalentTo(Option("Hello World")) should be (expectation.Equivalent( | |
Option("Hello World"), | |
Option("Hello World") | |
)) | |
} | |
"evaluate as not equivalent wben the expectation is checked against a different value" in { | |
Option("Hello Mundo").isExpectedToBeEquivalentTo(Option("Hello World")) should be (expectation.NotEquivalent( | |
Option("Hello Mundo"), | |
Option("Hello World") | |
)) | |
} | |
} | |
} | |
"Given two HList instances" when { | |
"the HList type is HNil it" should { | |
val input = HNil | |
"hold that both values must be equivalent" in { | |
input.isExpectedToBeEquivalentTo(input) shouldBe expectation.Equivalent( | |
input, | |
input | |
) | |
} | |
} | |
"two equal instances are checked the result" should { | |
val input = 42::HNil //"See"::"Spot"::"Run"::HNil | |
"be that they are equivalent" in { | |
//val exp = implicitly[EquivalenceExpectation[HNil]] | |
//val result = exp.apply(input, input) | |
input.isExpectedToBeEquivalentTo(input) shouldBe expectation.Equivalent( | |
input, | |
input | |
) | |
} | |
} | |
} | |
"Given two instances of a case class" when { | |
"two equal instances arec checked the result" should { | |
val spot = Dog("Spot", 5, "run") | |
val spot2 = Dog("Spot", 5, "run") | |
"be that they are equivalent" in { | |
// spot.isExpectedToBeEquivalentTo(spot2) shouldBe expectation.Equivalent( | |
// spot, | |
// spot2 | |
// ) | |
} | |
} | |
} | |
} | |
object EquivalenceExpectationSpecs { | |
case class Dog(name:String, bones:Int, action:String) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package experiments | |
import shapeless.Lazy | |
object expectation { | |
type Check[In] = (In,In) => Boolean | |
sealed trait ExpectationResultLike | |
sealed trait ExpectationResult[A] | |
sealed trait EquivalenceExpectationResult[A] extends ExpectationResult[A] | |
final case class Equivalent[A](actual:A,expected:A) extends EquivalenceExpectationResult[A] | |
final case class NotEquivalent[A](actual:A, expected:A) extends EquivalenceExpectationResult[A] | |
object ExpectationSyntax { | |
implicit class ExpectationOps[In](val actual: In) extends AnyVal { | |
def isExpectedToBeEquivalentTo(expected:In)(implicit expect: Lazy[EquivalenceExpectation[In]]): expect.value.Result = | |
expect.value(actual, expected) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment