如何使用FsCheck的固定项目列表创建生成器

Abe*_*bel 4 random f# fscheck

我最初尝试创建一个固定前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
Run Code Online (Sandbox Code Playgroud)

输出是(不知道null它来自哪里):

0:
<null>
9223372036854775807L
1:
<null>
-9223372036854775807L
2:
<null>
-9223372036854775807L
3:
<null>
1L
4:
<null>
-9223372036854775807L
Ok, passed 5 tests.
Run Code Online (Sandbox Code Playgroud)

我希望输出包含序列中的所有五个测试,最好是但不一定按顺序排列.我知道我可以使用测试数据提供程序使用NUnit(或任何单元测试系统),但我想知道我是否可以使用FsCheck(或者我是否应该这样做,也许这是一个坏主意).

我认为使用FsCheck是有用的,对于有多个函数参数的情况,我希望它能够详尽地测试我提供的极端情况参数的所有组合.使用FsCheck比使用testdata提供程序更容易.

Mar*_*ann 7

我不知道那是可能的,但你可以这样做:

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
Run Code Online (Sandbox Code Playgroud)

传递两个生成器Gen.oneof,因此每个生成器将生成大约一半的值.

Gen.elements应该从所提供的序列中的所有值中均匀地选取,因此它将使用例如0L20%的时间,但仅在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
Run Code Online (Sandbox Code Playgroud)

在这里,您使用xUnit.net的[<Theory>]功能定义参数化测试,并将其提供给您关注的五个角落案例.运行测试时,测试运行器将运行这五个测试用例.

此外,它会运行,MultiplyIdentityCornerCasesProperty因为它带有注释[<Property>],该函数只调用另一个函数.