我最初尝试创建一个固定前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.
我希望输出包含序列中的所有五个测试,最好是但不一定按顺序。我知道我可以使用测试数据提供程序使用NUnit(或任何单元测试系统),但我想知道我是否可以使用FsCheck(或者我是否应该这样做,或许这是一个坏主意)。
我认为使用FsCheck是有用的,对于有多个函数参数的情况,我希望它能够详尽地测试我提供的极端情况参数的所有组合。使用FsCheck比使用testdata提供程序更容易。
答案 0 :(得分:7)
我不知道那是可能的,但你可以这样做:
cic.declars
两个生成器传递给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
有20%的时间,但仅限于0L
使用Gen.oneof
时的一半。
换句话说,每个'特殊'值将产生50%* 20%= 10%的时间。
默认情况下,属性运行100个测试用例,因此平均而言,它应生成10个Gen.elements
值,10个0L
值,依此类推。这通常应该足够好。
如果不是,您可以随时执行以下操作:
Int64.MinValue
在这里,您使用xUnit.net的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
功能定义参数化测试,并将其提供给您关注的五个角落案例。运行测试时,测试运行器将运行这五个测试用例。
此外,它会运行[<Theory>]
,因为它是用MultiplyIdentityCornerCasesProperty
注释的,而该函数只是调用另一个函数。