美文网首页
函数可测试性问题浅析

函数可测试性问题浅析

作者: 吐思圈 | 来源:发表于2018-02-23 10:56 被阅读0次

不论是面向过程编程、面向对象编程亦或是函数式编程, 函数都是一个基本的单位,或者说是最小的功能单位。而在成千上万代码中,可维护和可测试是项目成败的关键。好的代码规范和全民皆兵式的Review是提升代码质量的利器,但无论怎么做代码单元测试完备性已然是行业公认的代码质量的量化的准则。本文仅针对函数的可测试性描述个人理解。

数学中,对而y = f(x)而言,一个输入值会有固定是输出值。比如说 sin(x)当输入为0,结果也固定是0。在函数式编程中,经常提到引用透明性,就是说函数结果即使用变量替换,程序运行结果依然是期望结果。我们期望的函数可测试性能够尽量做到数学上以及函数式编程里引用透明性特点。下面就例子进行说明。

我们在编写测试用例时,函数测试用例"最容易"编写,特别是对单个函数。实际上函数有外部依赖、外部服务的注入以及内部变量引入,远达不到引用透明性原则。因此在骨感的现实面前,函数测试远不是原本期望的样子。下面就函数可测试性浅谈下我们的尝试。

我们使用的方法:

  • 内部依赖配置、变量通过参数传入

  • 高阶函数作为入参

  • 外部依赖作为变量保存

    def readFileFromServer(fileType: String): List[String] = {
    val conn : FileServer = FileUtil.getConnect // server信息

    val cont = fileType match {
    case "mysql" => parseMysqlFile(conn)
    case "spark" => parseSparkFile(conn)
    case _ => Nil
    }

    conn.close() //

    ...... //cont解析 资源关闭等操作
    }

为了便于测试,我们需要规避函数内部的依赖,服务器连接信息从参数传入。

 def readFileFromServer(fileType: String, server: FileServer): List[String] = {
 
 val cont = fileType match {
     case "mysql" => parseMysqlFile(server)
     case "spark" => parseSparkFile(server)
     case  _ => Nil  
   }

...... .//cont解析 其它处理
}

上面函数parseMysqlFile,parseSparkFile的问题是 服务资源获取到之后的关闭问题。 面对此种情况通过借贷模式解决。即:

def using[A <: {def close() : Unit}, B](param: A)(f: A => B): B =
  try {
    f(param)
  } finally {
    if (param != null) { // scalastyle:ignore
      param.close()
    }
  }
  
 def readFileFromServer(fileType: String, server: FileServer): List[String] = {
 
 val cont = fileType match {
     case "mysql" => using(server)(parseMysqlFile(server))
     case "spark" => using(server)(parseSparkFile(server))
     case  _ => Nil  
   }

上例修改的副作用增加代码的复杂度,降低了代码覆盖率。
程序运行都有过程和边界控制的,对于模式匹配中的每一个pattern,在我们看来是没有必要覆盖的。即 using(server)(parseMysqlFile(server)) using(server)(parseSparkFile(server)) 代码覆盖率可以不完备。之前项目中代码覆盖率靓丽数据背后很多是反智操作。

def checkIn(product: List[String]) : Double = {
  val hk2cnyRatio = 0.9
  val sum =    ...... //扫描所有商品信息
   
   ratio * sum  //转换价格

}

上例涉及到汇率转换 hk2cnyRatio变量 是可变且可复用的。如果作为入参传入,不合理。hk2cnyRatio 需要作为类成员方法。

 val hk2cnyRatio = ??? // 从配置读取获取在线获取
 def checkIn(product: List[String]) : Double = {
  val sum =    ...... //扫描所有商品信息
   
   hk2cnyRatio * sum  //转换价格

}

按照可测试性而言,作为入参传入汇率解决测试问题。考虑它是函数成员变量,不作为入参。
以上方法:作为参数传递会造成函数入参过多;作为类成员在用例编写需要mock,才能让函数输出稳定。

因此,函数测试方法不一而足,隔离是基本原则。隔离外部依赖、内部实现使得代码精简易读,降低测试成本。

相关文章

  • 函数可测试性问题浅析

    不论是面向过程编程、面向对象编程亦或是函数式编程, 函数都是一个基本的单位,或者说是最小的功能单位。而在成千上万代...

  • 纯函数

    纯函数概念 相同输入必然得到相同输出,且函数必定有参数和返回值。 纯函数的好处 -可缓存 -可测试(如单元测试中,...

  • 函数式编程(二)—— 纯函数

    目录 纯函数纯函数的概念Lodash——纯函数的代表体验Lodash纯函数的好处可缓存可测试并行处理副作用【函数式...

  • qmake 手册 014 测试函数

    测试函数返回一个布尔值,我们可以在范围的条件部分测试该值。测试函数可以分为内置函数和函数库。 也可参阅 Repla...

  • 前端函数式编程浅析

    前端函数式编程浅析 前言 在浅析函数式编程之前,我们需要明确两个前导概念,即:编程范式(Programming P...

  • 函数式编程入门系列一

    函数引用透明性替换模型命令式,声明式与抽象纯函数纯函数产生可测试的代码并发代码可缓存管道与组合纯函数是数学函数 j...

  • 阅读《Python编程从入门到实践》Day14

    第十一章 编写函数或类时,还可为其编写测试。通过测试,可确定代码面对各种输入都能够按要求的那样工作。 1、测试函数...

  • pytest-fixture使用详解03(上)

    一、fixture的特点 在测试函数运行前后,由pytest执行的外壳函数,代码可定制用于将测试前后进行预备或清理...

  • 【JS】高阶函数与函数柯里化

    高阶函数 至少满足以下条件的函数: 接受一个或多个函数作为输入 输出一个函数JS高阶函数浅析[https://gi...

  • 2019-06-23

    go 字符串操作性能 浅析 go 性能分析 性能分析函数

网友评论

      本文标题:函数可测试性问题浅析

      本文链接:https://www.haomeiwen.com/subject/lpkeoxtx.html