2015-06-26 16 views
7

Xem xét một Liên minh phân biệt đối xử:Làm thế nào để dễ dàng lọc ra một trường hợp công đoàn phân biệt trong FsCheck?

type DU = | Foo of string | Bar of int | Baz of decimal * float | Qux of bool 

Tôi muốn tạo ra một danh sách các DU giá trị với FsCheck, nhưng tôi muốn không ai trong số các giá trị được của vụ án Qux.

vị này đã tồn tại:

let isQux = function Qux _ -> true | _ -> false 

nỗ lực đầu tiên

nỗ lực đầu tiên của tôi để tạo ra một danh sách các DU giá trị mà không có trường hợp Qux là một cái gì đó như thế này:

type DoesNotWork = 
    static member DU() = Arb.from<DU> |> Arb.filter (not << isQux) 

[<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesNotWork> |])>] 
let repro (dus : DU list) = 
    printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus 

Chạy điều này dường như tạo ra một tràn ngăn xếp, vì vậy tôi giả định rằng ha ppens đằng sau hiện trường là Arb.from<DU> gọi DoesNotWork.DU.

nỗ lực thứ hai

Sau đó, tôi cố gắng này:

type DoesNotWorkEither = 
    static member DU() = 
     Arb.generate<DU> 
     |> Gen.suchThat (not << isQux) 
     |> Arb.fromGen 

[<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesNotWorkEither> |])>] 
let repro (dus : DU list) = 
    printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus 

Cùng một vấn đề như trên.

giải pháp Verbose

Đây là giải pháp tốt nhất mà tôi đã có thể đưa ra cho đến nay:

type WithoutQux = 
    static member DU() = 
     [ 
      Arb.generate<string> |> Gen.map Foo 
      Arb.generate<int> |> Gen.map Bar 
      Arb.generate<decimal * float> |> Gen.map Baz 
     ] 
     |> Gen.oneof 
     |> Arb.fromGen 

[<Property(MaxTest = 10 , Arbitrary = [| typeof<WithoutQux> |])>] 
let repro (dus : DU list) = 
    printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus 

này hoạt động, nhưng có những nhược điểm sau:

  • Có vẻ như rất nhiều công việc
  • Nó không sử dụng sẵn cóChức năng, do đó có vẻ như vi phạm một cách tinh tế DRY
  • Nó không thực sự là bộ lọc, mà chỉ sản xuất các trường hợp mong muốn (do đó chỉ lọc theo thiếu sót).
  • Nó không phải là đặc biệt duy trì, bởi vì nếu tôi bao giờ thêm một trường hợp thứ năm để DU, tôi sẽ phải nhớ cũng thêm Gen cho trường hợp đó.

Có cách nào thanh lịch hơn để yêu cầu FsCheck lọc ra các giá trị Qux không?

Trả lời

5

Thay vì Arb.generate, mà cố gắng sử dụng các trường hợp đã đăng ký cho loại, đó là trường hợp bạn đang cố gắng xác định, gây ra một vòng lặp vô hạn, sử dụng Arb.Default.Derive() sẽ đi thẳng đến trình tạo dựa trên phản xạ.

https://github.com/fscheck/FsCheck/blob/master/src/FsCheck/Arbitrary.fs#L788-788

Đây là ví dụ một sai lầm phổ biến chúng ta sẽ có thể giải quyết ra khỏi hộp trong FsCheck: https://github.com/fscheck/FsCheck/issues/109


Các vấn đề cụ thể trong OP có thể được giải quyết như thế này:

type WithoutQux = 
    static member DU() = Arb.Default.Derive() |> Arb.filter (not << isQux) 

[<Property(MaxTest = 10 , Arbitrary = [| typeof<WithoutQux> |])>] 
let repro (dus : DU list) = 
    printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus 
4

dưới đây nên làm việc:

type DU = | Foo of string | Bar of int | Baz of decimal * float | Qux of bool 
let isQux = function Qux _ -> true | _ -> false 

let g = Arb.generate<DU> |> Gen.suchThat (not << isQux) |> Gen.listOf 

type DoesWork = 
    static member DU() = Arb.fromGen g 

[<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesWork> |])>] 
let repro (dus : DU list) = 
    printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus 

Lưu ý tôi đã sử dụng Gen.listOf ở cuối - có vẻ như FsCheck thất bại trong việc tạo ra bản thân một danh sách với các máy phát điện cho

+0

Vâng, điều đó phá vỡ vấn đề bằng cách tạo một 'Arbitrary ' thay vì một 'Arbitrary ', có thể sol đã vấn đề trước mắt của tôi, nhưng dường như không giải quyết được vấn đề cơ bản. –

+0

Không phải điều này đặt ra vấn đề mà chúng tôi không nhận được hỗ trợ thu hẹp cho DU? – Henrik

Các vấn đề liên quan