使用 FsCheck 生成记录

Using FsCheck to Generate Records

我想使用 FsCheck(与 XUnit)创建类型的记录:type QueryRequest = {Symbol: string; StartDate: DateTime; EndDate: DateTime} 其中 Symbol 仅限于 3 个选项 - ORCLIBMAAPLStartDateEndDate仅限于2000年1月1日2019年1月1日.

但是,我不清楚如何进行。我应该使用 Arb.generate<T>Arb.Default 或其他一些实用程序来生成和缩小测试用例吗?


更新 1

与生成记录的问题相关的后续问题可用 here

Original:
{ Symbol = ""
  StartDate = 8/9/2057 4:07:10 AM
  EndDate = 10/14/2013 6:15:32 PM }
Shrunk:
{ Symbol = ""
  StartDate = 8/9/2057 12:00:00 AM
  EndDate = 10/14/2013 12:00:00 AM }

更新 2

以下是测试套件代码:

namespace Parser

open Xunit
open FsCheck.Xunit
open DataGenerators

module Tests =
    [<Fact>]
    let ``sanity check`` () =
        let expected = true
        let actual = true
        Assert.Equal(expected, actual)

    [<Property(Arbitrary = [|typeof<StockTwitGenerator>|])>]
    let ``validate queries`` (q: QueryRecord) =
        q.EndDate > q.StartDate

Arb.filter 过滤给定 Arbitrary 实例的生成器和收缩器,以仅包含与给定过滤函数匹配的那些值。这应该可以帮助您满足您的需求。

https://fscheck.github.io/FsCheck/TestData.html#Useful-methods-on-the-Arb-module https://github.com/fscheck/FsCheck/blob/master/src/FsCheck/ArbitraryExtensions.fs#L17-17

当您具有将值限制为给定类型的所有允许值的一小部分的约束时,构造有效值比过滤更容易和更安全1

鉴于...

open FsCheck
open System

type QueryRequest = {Symbol: string; StartDate: DateTime; EndDate: DateTime}

...我们可以从为 Symbols 创建一个生成器开始:

let symbols = ["ORCL"; "IBM"; "AAPL"]
let symbol = Gen.elements symbols

和日期范围

let minDate = DateTime(2000, 1, 1)
let maxDate = DateTime(2019, 1, 1)
let dateRange = maxDate - minDate
let date =
    Gen.choose (0, int dateRange.TotalDays)
    |> Gen.map (float >> minDate.AddDays)

请注意 Gen.choose 只接受 int 范围。我们可以通过生成最大允许日期差异的随机偏移量然后映射回 DateTime

来解决

使用这些,我们可以为 QueryRequests...

构造一个生成器
let query =
    gen {
        let! s = symbol
        let! d1 = date
        let! d2 = date
        let startDate, endDate = if d1 < d2 then d1, d2 else d2, d1 
        return { Symbol = s; StartDate = startDate; EndDate = endDate }
    }

type MyGenerators =
  static member QueryRequest() =
      {new Arbitrary<QueryRequest>() with
          override _.Generator = query }

...注册...

Arb.register<MyGenerators>()

最后测试:

let test { Symbol = s; StartDate = startDate; EndDate = endDate } =
    symbols |> Seq.contains s && startDate >= minDate && endDate <= maxDate && startDate <= endDate

Check.Quick test

1 FsCheck 文档

Make sure there is a high chance that the predicate is satisfied.