我最初尝试创建一个固定前 5 个元素的生成器(并且在使用 Prop.forAll
的任何测试中,前五个总是会运行),但失败了。
现在我试图通过一个范围内的随机数据生成器和一个非随机数据的生成器(即固定序列)来简化这一点。它类似于 Gen.constant
,除了它不是一个值,而是一个值序列。
我有这个(简化的可重现示例,适用于 NUnit 和 xUnit):
[<Property(Verbose = true, MaxTest=5)>]
static member MultiplyIdentityCornerCases () =
Gen.elements [0L; -1L; 1L; Int64.MinValue; Int64.MaxValue]
|> Arb.fromGen
|> Prop.forAll <| fun x -> x = x * 1L
输出是(不知道
null
来自哪里):0:
<null>
9223372036854775807L
1:
<null>
-9223372036854775807L
2:
<null>
-9223372036854775807L
3:
<null>
1L
4:
<null>
-9223372036854775807L
Ok, passed 5 tests.
我希望输出包含序列中的所有五个测试,最好但不一定是按顺序。我知道我可以使用 testdata 提供程序使用 NUnit(或任何单元测试系统)来做到这一点,但我想知道我是否可以使用 FsCheck 来做到这一点(或者我是否应该这样做,也许这是一个坏主意)。
我认为使用 FsCheck 很有用,至于有多个函数参数的情况,我希望它彻底测试我给它的极端情况参数的所有组合。使用 FsCheck 这有望比使用 testdata 提供程序更容易。
最佳答案
我不知道这是可能的,但你可以这样做:
open System
open FsCheck
open FsCheck.Xunit
[<Property>]
let MultiplyIdentityCornerCases () =
Gen.oneof [
Gen.elements [Int64.MinValue; -1L; 0L; 1L; Int64.MaxValue]
Arb.generate ]
|> Arb.fromGen
|> Prop.forAll <| fun x -> x = x * 1L
两个生成器传递给
Gen.oneof
,所以每一个都会产生大约一半的值。Gen.elements
应该从提供的序列中的所有值中统一选择,因此它将使用例如0L
20% 的时间,但仅适用于 Gen.oneof
时的那一半用途 Gen.elements
.换句话说,这些“特殊”值中的每一个都将在 50% * 20% = 10% 的时间内生成。
默认情况下,一个属性会运行 100 个测试用例,因此平均而言,它应该生成 10
0L
值,10 Int64.MinValue
值等等。这通常应该足够好了。如果不是,你总是可以做这样的事情:
open System
open Xunit
open FsCheck
open FsCheck.Xunit
open Swensen.Unquote
[<Theory>]
[<InlineData(Int64.MinValue)>]
[<InlineData(-1L)>]
[<InlineData( 0L)>]
[<InlineData( 1L)>]
[<InlineData(Int64.MaxValue)>]
let MultiplyIdentityCornerCases x = x =! x * 1L
[<Property>]
let MultiplyIdentityCornerCasesProperty x =
MultiplyIdentityCornerCases x
在这里,您使用 xUnit.net 的
[<Theory>]
定义参数化测试。功能,并将您关心的五个极端情况提供给它。当您运行测试时,测试运行器将运行这五个测试用例。此外,它会运行
MultiplyIdentityCornerCasesProperty
因为它是用 [<Property>]
注释的,并且该函数只是调用另一个函数。
关于random - 如何为 FsCheck 创建具有固定项目列表的生成器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41065300/