2016-10-26 4 views
1

Ich habe eine Monad-ähnliche Art, die sehr ähnlich wie die Play Json Reads[T] Typ ist, genannt ReadYamlValue.So testen Sie eine Funktion wie Monad mit Katzen + Disziplin

trait ReadYamlValue[T] { 
    def read(json: YamlValue): ReadResult[T] 
    // ... methods include map, flatMap, etc 
} 

Ich habe eine Katze Monad Beispiel dafür:

implicit val ReadYamlValueMonad: Monad[ReadYamlValue] = new Monad[ReadYamlValue] { 
    override def flatMap[A, B](fa: ReadYamlValue[A])(f: A => ReadYamlValue[B]): ReadYamlValue[B] = { 
    fa flatMap f 
    } 
    override def tailRecM[A, B](a: A)(f: A => ReadYamlValue[Either[A, B]]): ReadYamlValue[B] = { 
    ReadYamlValue.read[B] { yaml => 
     @tailrec def readB(reader: ReadYamlValue[Either[A, B]]): ReadResult[B] = { 
     reader.read(yaml) match { 
      case Good(Left(nextA)) => readB(f(nextA)) 
      case Good(Right(b)) => Good(b) 
      case Bad(error) => Bad(error) 
     } 
     } 
     readB(f(a)) 
    } 
    } 
    override def pure[A](x: A): ReadYamlValue[A] = ReadYamlValue.success(x) 
} 

Und dann wollte ich es mit dem MonadLaws und Scalacheck testen.

class CatsTests extends FreeSpec with discipline.MonadTests[ReadYamlValue] { 
    monad[Int, Int, Int].all.check() 
} 

Aber ich bekomme:

could not find implicit value for parameter EqFA: cats.Eq[io.gloriousfuture.yaml.ReadYamlValue[Int]] 

Wie definiere ich Eq für das, was effektiv eine Funktion? Vergleich der Gleichheit einer Funktion scheint wie es ist nicht, was ich will ... Ist meine ReadYamlValue Klasse keine Monade oder sogar ein Functor für diese Angelegenheit?

Eine Möglichkeit, dies zu tun, ist eine beliebige Probe zu erzeugen und die Gleichheit des Ergebnisses vergleichen:

implicit def eqReadYaml[T: Eq: FormatYamlValue: Arbitrary]: Eq[ReadYamlValue[T]] = { 
    Eq.instance { (a, b) => 
    val badYaml = arbitrary[YamlValue].getOrThrow 
    val goodValue = arbitrary[T].getOrThrow 
    val goodYaml = Yaml.write(goodValue) 
    Seq(badYaml, goodYaml).forall { yaml => 
     (a.read(yaml), b.read(yaml)) match { 
     case (Good(av), Good(bv)) => Eq.eqv(av, bv) 
     case (Bad(ae), Bad(be)) => Eq.eqv(ae, be) 
     case _ => false 
     } 
    } 
    } 
} 

Aber dies scheint, wie es die Definition der Gleichheit ein Bit ausweicht. Gibt es einen besseren oder kanonischeren Weg, dies zu tun?

Antwort

1

Es ist wie mit Arbitrary Instanzen sieht, wie Circe tut es:

https://github.com/travisbrown/circe/blob/master/modules/testing/shared/src/main/scala/io/circe/testing/EqInstances.scala

Sie nehmen einen Strom von 16 Proben und die Ergebnisse vergleichen.

+0

Der einzige Nachteil dabei scheint zu sein, dass es unwahrscheinlich ist, dass ein einzelnes Beispiel erzeugt wird, dass beide erfolgreich "lesen" werden. Ich befürchte, dass alle 16 Fehler Ergebnisse sein werden und der Erfolgsfall wird nicht getestet werden. Ist das für das Testen der 'MonadLaws' sehr wichtig? –

+0

Für die Decodierungsseite sollte circe wirklich einige willkürliche 'A'-Werte erzeugen, diese dann codieren und dann sicherstellen, dass die Decoder das gleiche tun. –

Verwandte Themen