加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 以模块化方式创建Specs2匹配器

发布时间:2020-12-16 08:57:39 所属栏目:安全 来源:网络整理
导读:我有函数A =双.我想检查两个这样的函数是否为给定的值集给出相同的结果(使用现有的beCloseTo匹配器的公差). 我希望能写: type TF = A = Double(f: TF) must computeSameResultsAs(g: TF,tolerance: Double,tests: Set[A]) 我想以模块化的方式构建这个匹配器
我有函数A =>双.我想检查两个这样的函数是否为给定的值集给出相同的结果(使用现有的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,args: Set[A]): Matcher[A => Double] = 
  args.map(beCloSEOnArg(ref,tolerance,_)).reduce(_ and _)

def beCloSEOnArg[A](ref: A => Double,arg: A): Matcher[A => Double] = 
  closeTo(ref(arg),tolerance) ^^ ((_: A => Double).apply(arg))

这比Eric的解决方案短得多,但没有提供良好的失败信息.我希望能够在第二种方法中重命名映射值.像下面的东西(不编译).

def beCloSEOnArg[A](ref: A => Double,tolerance) ^^ ((_: A => Double).apply(arg) aka "result on argument " + arg)

解决方法

如果你想用第二个版本编写东西,你需要创建一个新的Matcher类来封装beCloseTo匹配器的功能:

def computeSameResultsAs[A](g: A => Double,tolerance: Double = 0.0,values: Seq[A] = Seq()) = TFMatcher(g,values)

case class TFMatcher[A](g: A => Double,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,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)          
}

现在,让我们看看如何在apply方法中重用beCloseTo匹配器:

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,f)
 }

在上面的代码中,我们应用一个返回MatcherResult to a sequence of values的函数:

((v: A) => beCloseTo(g(v) +/- tolerance).apply(theValue(f.value(v)))).forall(values)

注意:

> f是可预期的[A => Double]所以我们需要把它的实际值用来使用它
>类似地,我们只能将Expectable [T]应用于Matcher [T],因此我们需要使用方法theValue将f.value(v)转换为Expectable [Double](来自MustExpectations特征)

最后,我们在有forall匹配的结果时,我们可以使用以下方法自定义结果消息:

>构建MatchResult的继承结果方法(任何匹配器的apply方法应返回的内容)
>如果beCloseTo的执行成功,则传递一个布尔值:.isSuccess
>根据输入和beCloseTo匹配的结果消息传递格式良好的“ok”和“ko”消息
>传递用于首先进行匹配的Expectable:f,以便最终结果具有MatchResult类型[A =>双]

我不确定我们可以根据您的要求获得更多模块化.在我看来,我们在这里做的最好的事情就是重复使用带有forall的beCloseTo.

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,3))          
}

def computeSameResultsAs[A](ref: A => 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,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,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,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,the 1st element is failing: the value '1.0' is not close to 2.0 +/- 0.5

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读