11.2.3 测试组合函数

在第11.1.2 节,我们讨论跟踪代码中的依赖关系时,使用的C# 方法,类似于上两个示例中的F# 函数,演示函数式编程使得更容易识别函数做什么,访问什么数据。这不仅在写代码时非常有用,而且在测试时也极其有用。

在第11.1 节,我们写过一个命令式方法,打印出由多字组成的名字,但是,它有副作用,会从作为参数传递进来的可变列表中删除元素。只要我们以后不再使用这个列表,就不会引起任何问题。对这个方法的任何单元测试以检查打印输出,都会成功。

使这个方法棘手的是,如果我们把它与其他同样正确的方法连一起使用时,可能会得到意想不到的结果,因此,想彻底测试命令代码很难。原则上,我们应该测试的是,每个方法只做它应该做的,且仅此。不幸的是,“且仅此”部分真的难以测试,因为任何一段代码都可以访问并修改共享的可变状态的任何一部分。

而在函数式编程中,我们不会修改任何共享状态,因此,我们只需要验证,对于所有给定的输入,函数返回正确的结果。这也说明,当我们把两个测试过的函数放在一起使用时,只要测试这个组合有相应的结果:并不需要验证其中的函数不会以微妙的方式破坏了对方的数据。清单11.11 显示的这种测试,看起来完全没有意义,但是想象一下,如果我们使用List<T>,而不是不可变的F# 列表,会是什么样子。

清单11.11 测试两个有副作用的函数(F#)

[<Fact>]

let partitionThenLongest() =

lettest = ["Seattle"; "New York"; "Grantchester"]

letexpected = ["New York"], ["Seattle"; "Grantchester"]

letactualPartition = partitionMultiWord(test)  | [1]

letactualLongest = getLongest(test)          |

Assert.Equal(expected,actualPartition)      | [2]

Assert.Equal("Grantchester",actualLongest)  |

可以发现,单元测试顺序地运行两个函数[1],但只使用部分结果,验证结果是合我们的期望值[2]。这样,函数调用是独立的,如果它们不包含任何副作用,我们可以自由地改变调用的顺序。在函数世界中,这个单元测试根本不需要:我们已经为每个单独的函数写过单元测试,而这个测试并没有验证任何额外的行为。

然而,如果我们想要使用可变的List<T> 类型,写类似的代码,这个测试可能会捕获到我们在第11.1 节发现过的错误。如果partitionMultiWord 函数修改了值 test 所引用的列表,比如,删除所有单字的名字,那么,第二次调用的结果就不可能是测试预期的“Grantchester”。这是关于函数式代码重要的一般观点:如果我们很好地测试了所有的基本部分,再测试组合的代码,那么,我们就不需要在新的配置中,测试这些基本部分是否仍然有正确的行为。

到目前为止,我们已经讨论了函数式程序的重构和测试。我们发现,一等函数能够减少代码重复,不可变的数据结构有助于我们了解代码在做什么,而且,减少了测试两段代码可能会互相干扰的需要。

本章的剩余部分将会讨论如果代码被执行时,如何能够利用这一优势,使代码更高效。首先,我们需要了解何时有一定的灵活性,F# 和C# 如何决定何时执行代码。

时间: 2024-10-12 20:40:10

11.2.3 测试组合函数的相关文章

javascript柯里化及组合函数~

大家是不是看我上篇博文有点蒙.用的的curry和compose是什么鬼,怎么那么神奇.上篇博文也是主要用到了这两个函数.那今天我们来聊一下curry和compose,这两个东西是函数式编程很重要的东西,基本每个稍微复杂点的例子都要涉及这两个函数.什么是curry呢?---函数柯里化.就是这个东西了.举一个简单的例子. var _console=curry(function(x){ console.log(x); }) _console("hello"); //hello 其实就这个作用

UVALive-7304 - Queue of Soldiers 【动态规划】【组合函数】【好题】

UVALive- 7304 - Queue of Soldiers 题目链接:7304 题目大意:士兵过山洞,必须以类似7 6 5 4 3 2 1顺序过.在第i个人之后,比i高的人都会被杀死,问如果要杀死k个人,有几种排队方法. 题目思路:先将士兵的身高离散化.假设N表示不同身高的数目.cnt[i] 表示i这个身高的人有多少个.(i的范围为1~N)sum[i]表示小于等于该身高段的士兵数目 然后开始dp,dp[i][j]表示已经到第i个士兵,已经死了j个人的方法数. 第三维遍历,q表示,第i+1

C++11新特性:Lambda函数(匿名函数)

声明:本文参考了Alex Allain的文章http://www.cprogramming.com/c++11/c++11-lambda-closures.html 加入了自己的理解,不是简单的翻译 C++11终于知道要在语言中加入匿名函数了.匿名函数在很多时候可以为编码提供便利,这在下文会提到.很多语言中的匿名函数,如C++,都是用Lambda表达式实现的.Lambda表达式又称为lambda函数.我在下文中称之为Lambda函数. 为了明白Lambda函数的用处,请务必先搞明白C++中的自动

ctype.h——使用测试字符函数

islower()                       是否为小写字母 isupper()                      是否为大写字母 isalpha()                       是否为字母 isalnum()                     是否为字母或十进制数字 iscntrl()                       是否为控制字符 isprint()                      是否为可打印字符(包含空格) isgra

Linux-0.11内核内存管理get_free_page()函数分析

/* *Author : DavidLin*Date : 2014-11-11pm*Email : [email protected] or [email protected]*world : the city of SZ, in China*Ver : 000.000.001*history : editor time do 1)LinPeng 2014-11-11 created this file! 2)*/Linux-0.11内存管理模块是源代码中比较难以理解的部分,现在把笔者个人的理解

[深入JUnit] 为什么别测试private函数

[深入JUnit] 为什么别测试private函数 摘自http://www.tuicool.com/articles/iumaayJ 时间 2016-03-28 10:58:03 SegmentFault 原文  https://segmentfault.com/a/1190000004700616 主题 JUnit 比如说,Bird是我们要测试的class,它有public, protected,以及private的方法. // 文件位置:src/test/sample/Bird.java

【C++11】新特性——Lambda函数

本篇文章由:http://www.sollyu.com/c11-new-lambda-function/ 文章列表 本文章为系列文章 [C++11]新特性--auto的使用 http://www.sollyu.com/c11-new-features-auto/ [C++11]新特性--Lambda函数 http://www.sollyu.com/c11-new-lambda-function/ 说明 在标准 C++,特别是当使用 C++ 标准程序库算法函数诸如 sort 和 find,用户经常

OpenGL笔记8 组合函数

今天介绍关于OpenGL显示列表的知识.本课内容并不多,但需要一些理解能力.在学习时,可以将显示列表与C语言的“函数”进行类比,加深体会. 我们已经知道,使用OpenGL其实只要调用一系列的OpenGL函数就可以了.然而,这种方式在一些时候可能导致问题.比如某个画面中,使用了数千个多边形来表现一个比较真实的人物,OpenGL为了产生这数千个多边形,就需要不停的调用glVertex*函数,每一个多边形将至少调用三次(因为多边形至少有三个顶点),于是绘制一个比较真实的人物就需要调用上万次的glVer

JavaScript-原型&amp;原型链&amp;原型继承&amp;组合函数

小小的芝麻之旅: 今天学习了js的原型,要说原型,我们先简单说一下函数创建过程. 原型 每个函数在创建的时候js都自动添加了prototype属性,这就是函数的原型,原型就是函数的一个属性,类似一个指针.原型在函数的创建过程中由js编译器自动添加. <script type="text/javascript"> function Flower(name,area) { this.name=name; this.area=area; this.showName=myName;