我正在使用FsCheck在F#中进行一些属性测试。因此,无论输入参数如何,我都希望保证某些条件始终存在。
考虑我为float
值定义一个简单的身份函数。
let floatId (x : float) = x
然后我定义了这个函数的测试,我知道应该总是这样:
let ``floatId returns input float`` x = floatId x = x
这是一个简单的测试,我只是检查调用我的float identity函数返回与输入float相同的内容。
然后我将此功能插入FsCheck:
Check.Quick ``floatId returns input float``
不幸的是,这个属性测试失败了!
Falsifiable, after 21 tests (0 shrinks) (StdGen (1872424299,296201373)): Original: nan
当然,回顾过去,很明显会发生这种情况,我们知道nan <> nan
。
由于F#中的结构比较,这可能会导致(略微)涉及集合的更复杂的测试案例。
如果我为浮点列表设计了类似的函数:
let listFloatId (lst : float list) = lst
let ``listFloatId returns input float list`` lst = listFloatId lst = lst
Falsifiable, after 6 tests (3 shrinks) (StdGen (1874889363,296201373)): Original: [nan; 2.0; 2.25; 4.940656458e-324] Shrunk: [nan]
再次出现同样问题!
显然,我可以通过创建自己的相等测试函数来解决这个问题,这对于float
值来说很好,但是扩展到像list
之类的集合变得更加复杂,因为我必须开始使用List.forall2
和我的自定义相等函数,并且通常将我的代码专门用于每个单独的集合类型。
在F#中是否有解决此问题的一般方法?
答案 0 :(得分: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
这一次:
Ok, passed 100 tests. Ok, passed 100 tests.
(我问,并回答这个问题,因为上面的属性是在FSharp Software Foundation Slack频道中提出的,我认为将这个解决方案记录下来是有用的。我几乎没有在网上提到这个功能超出LanguagePrimitives
模块的文档。