F#探险之旅(七):在F#中进行单元测试

简介:

单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常情况下,一个单元测试(用例)用于判断某个特定条件(或场景)下特定函数的行为。如果想对单元测试的好处有更多的了解,可以看一下单元测试实战

在.NET社区内,NUnit无疑是最经典的单元测试工具,要了解它的用法,建议看一下园子里的一篇很棒的文章NUnit详细使用方法。本文对此不再赘述。另外MbUnit作为后起之秀,也很值得一试。

在F#中, LOPLanguage-Oriented Programming)是它的一个亮点,而FsUnit则是LOP的一个很好的实践。FsUnit使用F#开发,用它编写的测试用例会接近于自然语言(英语),在其中我们也可以看到F#对函数进行组合的强大威力。

在本文中,我将通过简单的例子分别对NUnit和FsUnit的基本用法进行介绍。假定我们在开发一个类库MyFsLib,其中有一个模块mathHelper,里面有一些关于数学的函数,现在要做的就是测试这些函数。mathHelper的代码如下:

复制代码
F# Code - mathHelper的签名
#light
module MyFsLib.MathHelper

/// 获取一个浮点数的平方值
val square: float -> float

/// 获取一个浮点数的立方值
val cube: float -> float

/// 判断一个整数是否为偶数
val isEven: int -> bool

/// 判断一个整数是否为奇数
val isOdd: int -> bool

/// 获取不大于指定正整数的质数数组
val generatePrimes: int -> int array
复制代码

 

F# Code - mathHelper的实现


使用NUnit进行单元测试

不要害怕,由于F#植根于.NET平台的本性,你会发现这些测试用例代码都是那么眼熟。

需要废话的是,先添加对”nunit.framework.dll”的引用,而且要为测试类添加一个无参构造函数。

复制代码
F# Code - NUnit tester
#light
namespace NUnitTester

open NUnit.Framework
open MyFsLib

[<TestFixture
>]
type TestCases = class
new() = {}

[<Test
>]
member this.TestSquare() =
Assert.AreEqual(
0, MathHelper.square(0.0))
Assert.AreEqual(
4, MathHelper.square(2.0))

[<Test
>]
member this.TestGeneratePrimes() =
let primesLessThan2 = MathHelper.generatePrimes(1)
CollectionAssert.IsEmpty(primesLessThan2)

let primesNotGreaterThan2 = MathHelper.generatePrimes(2)
CollectionAssert.IsNotEmpty(primesNotGreaterThan2)
CollectionAssert.Contains(primesNotGreaterThan2,
2)
Assert.AreEqual(
1, primesNotGreaterThan2.Length)

let primesNotGreaterThan10 = MathHelper.generatePrimes(10)
CollectionAssert.IsNotEmpty(primesNotGreaterThan10)
CollectionAssert.Contains(primesNotGreaterThan10,
7)
Assert.AreEqual(
4, primesNotGreaterThan10.Length)

// Other testcases
end
复制代码


这里只编写了对函数square和generatePrimes的测试。如果你在C#中用过NUnit,看这样的代码就没有任何问题了。测试结果为:

使用FsUnit进行单元测试

FsUnit是一个Specification测试框架。它的目标是将单元测试和行为(函数)的规格尽量简化,并以函数式的风格代替命令式风格的测试代码。

Specification可以翻译为规格说明,就是说测试代码实际上是对待测代码的一条条规格说明。比如对函数square,它求一个数的平方,那么一条规格可以是:”square(2) should equal 4”。好了,惊喜就要来了:

复制代码
F# Code - FsUnit tester
#light

open FsUnit
open MyFsLib

let squareSpecs =
specs
"Test square" [
spec
"square(0) should equal 0"
(MathHelper.square(
0.0) |> should equal 0.0) // Pass
spec "square(2) should equal 2"
(MathHelper.square(
2.0) |> should equal 2.0) // Fail
]

let generatePrimes1Specs =
specs
"Test generatePrimes" [
spec
"generatePrimes(1).Length should equal 0"
(MathHelper.generatePrimes(
1).Length |> should equal 0)
]

let generatePrimes2Specs =
specs
"Test generatePrimes" [
spec
"generatePrimes(2).Length should equal 1"
(MathHelper.generatePrimes(
2).Length |> should equal 1)
spec
"generatePrimes(2) should contain 2"
(MathHelper.generatePrimes(
2) |> should contain 2)
]

let generatePrimes10Specs =
specs
"Test generatePrimes" [
spec
"generatePrimes(10).Length should equal 4"
(MathHelper.generatePrimes(
10).Length |> should equal 4)
spec
"generatePrimes(10) should contain 7"
(MathHelper.generatePrimes(
10) |> should contain 7)
spec
"generatePrimes(10) should not contain 9"
(MathHelper.generatePrimes(
10) |> should not' (contain 9))
]

printfn
"%s" (Results.summary())
复制代码


这里没有Assert,有的是should,这两个词给人的感觉可大不一样,而且通过函数的组合,我们可以写出should equal这样的“句子”。这里的测试代码已经比较接近自然语言了。

一条spec就是一条规格说明,它说明待测的函数具有什么样的规格,我们把这些都放在specs中,测试结束后,使用Results.summary函数来显示测试结果:
 
如果对FsUnit感兴趣,可以到http://code.google.com/p/fsunit/这里来看看。感觉目前它还有些欠缺,比如没有像CollectionAssert这样的测试类,接下来看看能不能扩展一下。

小结

本文介绍了在F#中如何使用NUnit和FsUnit进行单元测试。可以看到两者都很简单,前者简单是因为能很好地延续在C#中的方式,迁移过来不要费多大力气;后者简单是因为它接近自然语言,看起来很亲切。FsUnit值得关注,除了单元测试本身,我们还可以通过它来了解Language-Oriented Programming的相关知识。

(要了解本人所写的其它F#随笔请查看 F#系列随笔索引

参考

《Foundations of F#》 by Robert Pickering
http://code.google.com/p/fsunit/


本文转自一个程序员的自省博客园博客,原文链接:http://www.cnblogs.com/anderslly/archive/2008/11/18/fsharp-adventures-unit-test.html,如需转载请自行联系原作者。

目录
相关文章
|
8月前
|
Java 测试技术 Python
关于如何写自动化测试框架,看完本章你会有收获
关于如何写自动化测试框架,看完本章你会有收获
81 0
|
10月前
|
Web App开发 存储 前端开发
前端自动化测试之葵花宝典
前端自动化测试之葵花宝典
|
11月前
|
消息中间件 NoSQL Java
谈谈如何使用好单元测试这把武器
本文作者结合我们日常的工作,讨论如何使用好单元测试这把武器。
668 0
|
IDE 测试技术 Go
字节跳动青训营Day02 - 并发编程,单元测试
1.单元测试初步讲解 Go 语言推荐测试文件和源代码文件放在同一目录下,测试文件以 _test.go 结尾。比如,当前 package 有 calc.go 一个文件,我们想测试 calc.go 中的 Add 和 Mul 函数,那么应该新建 calc_test.go 作为测试文件
120 0
字节跳动青训营Day02 - 并发编程,单元测试
|
测试技术 程序员 API
从零开始搞基建(4)——单元测试
  单元测试有助于避免尴尬、耗时的错误,将测试作为安全网只是一部分,更大部分是将测试表达为代码的思考过程。   接下来的内容提炼自《单元测试的艺术(第2版)》和《有效的单元测试》两本书。
|
自然语言处理 Java 测试技术
告别祈祷式编程|单元测试在项目里的正确落地姿势
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Jav...
146 1
|
Java 测试技术 数据库
EnvironmentPostProcessor怎么做单元测试?阿里P7告诉你
从Spring Boot 1.3开始,我们可以在应用程序上下文刷新之前使用EnvironmentPostProcessor来自定义应用程序的Environment。Environment表示当前应用程序运行的环境,它可以统一访问各种属性源中的属性,如属性文件、JVM系统属性、系统环境变量和Servlet上下文参数。使用EnvironmentPostProcessor可以在bean初始化之前对Environment进行修改。
207 0
|
SQL JSON Java
谈一谈单元测试
写在前面对于我们开发人员来说,单元测试一定不会陌生,但在各种原因下会被忽视,尤其是在我接触到的项目中,提测阶段发现各种各样的问题,我觉得有必要聊一下单元测试。对于单元测试到底有没有存在的必要,这里不是我想要说的重点。有兴趣的可以去了解一下:【单元测试和TDD】【单元测试到底是什么】为了写而写的单元测试没什么价值,但一个好的单元测试带来的收益是非常客观的。问题是怎么去写好单元测试?怎么去驱动写好单元
605 0
谈一谈单元测试
|
Java 测试技术 程序员
单元测试经典三问:是什么,为什么,怎么做?
编写合格的单元测试可以说是 Java 程序员的基本功。 很多公司对但单测覆盖率都会有要求,通常要求在 60% 到 90% 不等。 但是很多同学对单元测试或多或少有一些抵触,对如何写出“标准”的单元测试代码存在疑问。 有些同学编写单元测试,纯粹是应付工作,完全起不到单测应该起到的作用。 本文解答单元测试的三个基本问题,即单元测试是什么,为什么编写单元测试,怎么编写单元测试?
763 0
单元测试经典三问:是什么,为什么,怎么做?
|
Dubbo 测试技术 应用服务中间件
单测的心得体会
单测是规范的软件开发流程中的必不可少的环节之一。再伟大的程序员也难以避免自己不犯错,不写出有BUG的程序。单测就是用来检测BUG的。Java阵营中,JUnit和TestNG是两个知名的单测框架。不过,用Java写单测实在是很繁琐,mock各种参数需要大量时间,所以单测试往往被认为鸡肋,会耗费很多时间。最近实习的过程中遇到一些坑,发现养成一个良好的单测习惯对于系统化开发是很有必要的。

相关实验场景

更多