在F#中针对nan值进行属性测试的简洁通用方法是什么?

The*_*ght 5 f# unit-testing fscheck

我正在使用FsCheck在F#中进行一些属性测试.因此,无论输入参数如何,我都希望保证某些条件始终存在.

考虑我为float值定义一个简单的标识函数.

let floatId (x : float) = x
Run Code Online (Sandbox Code Playgroud)

然后我定义了这个函数的测试,我知道应该总是这样:

let ``floatId returns input float`` x = floatId x = x
Run Code Online (Sandbox Code Playgroud)

这是一个简单的测试,我只是检查调用我的float identity函数返回与输入float相同.

然后我将此函数插入​​FsCheck:

Check.Quick ``floatId returns input float``
Run Code Online (Sandbox Code Playgroud)

不幸的是,这个属性测试失败!

Falsifiable, after 21 tests (0 shrinks) (StdGen (1872424299,296201373)):
Original: 
nan
Run Code Online (Sandbox Code Playgroud)

当然,回顾过去,很明显这将会发生,我们知道nan <> nan.

由于F#中的结构比较,这可能会困扰(稍微)涉及集合的更复杂的测试案例.

如果我为浮点列表设计一个类似的函数:

let listFloatId (lst : float list) = lst

let ``listFloatId returns input float list`` lst = listFloatId lst = lst
Run Code Online (Sandbox Code Playgroud)
Falsifiable, after 6 tests (3 shrinks) (StdGen (1874889363,296201373)):
Original:
[nan; 2.0; 2.25; 4.940656458e-324]
Shrunk:
[nan]
Run Code Online (Sandbox Code Playgroud)

同样的问题了!


显然我可以通过创建我自己的相等测试函数来解决这个问题,这对于float值很好但扩展到集合变得更加复杂,list因为我必须开始使用List.forall2我的自定义相等函数,并且通常将我的代码专门化到每个单独的集合类型.

在F#中有解决这个问题的一般方法吗?

The*_*ght 5

您可以使用该LanguagePrimitives.GenericEqualityER功能解决此问题.这使用Equivalance Relation语义检查相等性.该函数实际上是站点比较[nan]列表的具体示例.

测试用例可以这样定义:

let ``ER : floatId returns input float`` x = LanguagePrimitives.GenericEqualityER (floatId x)  x

let ``ER : listFloatId returns input float list`` lst = LanguagePrimitives.GenericEqualityER (listFloatId lst)  lst
Run Code Online (Sandbox Code Playgroud)

这次:

Ok, passed 100 tests.
Ok, passed 100 tests.
Run Code Online (Sandbox Code Playgroud)

(我问,并回答这个问题,因为上面的属性是在FSharp Software Foundation Slack频道中提出的,我认为将此解决方案记录下来是有用的.我几乎没有在文档之外在线提及此功能在LanguagePrimitives模块上).