zig*_*tar 8 specs scala specs2
我有功能A => Double.我想检查两个这样的函数是否beCloseTo为给定的值集给出相同的结果(使用现有匹配器的公差).
我希望能够写:
type TF = A => Double
(f: TF) must computeSameResultsAs(g: TF,tolerance: Double, tests: Set[A])
我想以模块化的方式构建这个匹配器,而不是简单地Matcher[TF]从头开始编写.
如果我能写的话可能会更好:
(f: TF) must computeSameResultsAs(g: TF)
               .withTolerance(tolerance)
               .onValues(tests: Set[A])
我还想在匹配器失败时获得合理的描述.
睡了之后我想出了以下内容.
def computeSameResultsAs[A](ref: A => Double, tolerance: Double, args: Set[A]): Matcher[A => Double] = 
  args.map(beCloseOnArg(ref, tolerance, _)).reduce(_ and _)
def beCloseOnArg[A](ref: A => Double, tolerance: Double, arg: A): Matcher[A => Double] = 
  closeTo(ref(arg), tolerance) ^^ ((_: A => Double).apply(arg))
这比Eric的解决方案短得多,但没有提供良好的失败信息.我希望能够在第二种方法中重命名映射值.像下面的东西(不编译).
def beCloseOnArg[A](ref: A => Double, tolerance: Double, arg: A): Matcher[A => Double] = 
  closeTo(ref(arg), tolerance) ^^ ((_: A => Double).apply(arg) aka "result on argument " + arg)
如果你想用第二个版本编写东西,你需要创建一个Matcher封装beCloseTomatcher 功能的新类:
def computeSameResultsAs[A](g: A => Double, 
                            tolerance: Double = 0.0, 
                            values: Seq[A] = Seq()) = TFMatcher(g, tolerance, values)
case class TFMatcher[A](g: A => Double, 
                        tolerance: Double = 0.0, 
                        values: Seq[A] = Seq()) extends Matcher[A => Double] {
  def apply[S <: A => Double](f: Expectable[S]) = {
    // see definition below
  }
  def withTolerance(t: Double) = TFMatcher(g, t, values)
  def onValues(tests: A*) = TFMatcher(g, tolerance, tests)
}
这个类允许使用你以后的语法:
val f = (i: Int) => i.toDouble
val g = (i: Int) => i.toDouble + 0.1
"f must be close to another similar function with a tolerance" in {
  f must computeSameResultsAs[Int](g).withTolerance(0.5).onValues(1, 2, 3)          
}
现在,让我们看看如何beCloseTo在apply方法中重用匹配器:
def apply[S <: A => Double](f: Expectable[S]) = {
  val res = ((v: A) => beCloseTo(g(v) +/- tolerance).apply(theValue(f.value(v)))).forall(values)
  val message = "f is "+(if (res.isSuccess) "" else "not ")+
                "close to g with a tolerance of "+tolerance+" "+
                "on values "+values.mkString(",")+": "+res.message
   result(res.isSuccess, message, message, f)
 }
在上面的代码中,我们应用一个函数返回一个MatcherResult 值序列:
((v: A) => beCloseTo(g(v) +/- tolerance).apply(theValue(f.value(v)))).forall(values)
注意:
f是一个Expectable[A => Double]所以我们需要采取它的实际value能够使用它
同样我们只能应用一个Expectable[T],Matcher[T]所以我们需要使用该方法theValue转换f.value(v)为Expectable[Double](来自MustExpectations特征)
最后,我们在得到forall匹配结果时,可以使用以下方法自定义结果消息:
result构建a 的继承方法MatchResult(apply任何方法Matcher应该返回什么
如果执行beCloseTo成功则传递一个布尔值:.isSuccess
根据输入和beCloseTo匹配的结果消息传递格式良好的"ok"和"ko"消息
传递它Expectable用于在第一个位置进行匹配:f,以便最终结果具有类型MatchResult[A => Double]
我不确定我们可以根据您的要求获得更多模块化.在我看来,我们可以在这里做的最好是重用beCloseTo用forall.
UPDATE
较短的答案可能是这样的:
val f = (i: Int) => i.toDouble
val g = (i: Int) => i.toDouble + 1.0
"f must be close to another similar function with a tolerance" in {
  f must computeSameResultsAs[Int](g, tolerance = 0.5, values = Seq(1, 2, 3))          
}
def computeSameResultsAs[A](ref: A => Double, tolerance: Double, values: Seq[A]): Matcher[A => Double] = (f: A => Double) => {
  verifyFunction((a: A) => (beCloseTo(ref(a) +/- tolerance)).apply(theValue(f(a)))).forall(values)
}
上面的代码创建了一条失败消息:
In the sequence '1, 2, 3', the 1st element is failing: 1.0 is not close to 2.0 +/- 0.5
这几乎应该是开箱即用的.缺少的部分是由隐式转换A => MatchResult[_]到Matcher[A](这我会添加到下一个版本):
implicit def functionResultToMatcher[T](f: T => MatchResult[_]): Matcher[T] = (t: T) => {
  val result = f(t)
  (result.isSuccess, result.message)
}
您可以使用foreach而不是forall如果您想要获得所有失败:
1.0 is not close to 2.0 +/- 0.5; 2.0 is not close to 3.0 +/- 0.5; 3.0 is not close to 4.0 +/- 0.5
更新2
这每天都变得更好.使用最新的specs2快照,您可以编写:
def computeSameResultsAs[A](ref: A => Double, tolerance: Double, values: Seq[A]): Matcher[A => Double] = (f: A => Double) => {
  ((a: A) => beCloseTo(ref(a) +/- tolerance) ^^ f).forall(values)
}   
更新3
现在使用最新的specs2快照,你可以写:
def computeSameResultsAs[A](ref: A => Double, tolerance: Double, values: Seq[A]): Matcher[A => Double] = (f: A => Double) => {
  ((a: A) => beCloseTo(ref(a) +/- tolerance) ^^ ((a1: A) => f(a) aka "the value")).forall(values)
}   
失败消息将是:
In the sequence '1, 2, 3', the 1st element is failing: the value '1.0' is not close to 2.0 +/- 0.5
| 归档时间: | 
 | 
| 查看次数: | 1111 次 | 
| 最近记录: |