Как я могу игнорировать значение дискриминационного объединенного случая в утверждении FsUnit?

Как я могу игнорировать значение дискриминационного объединенного случая в утверждении FsUnit?

Взять, к примеру:

type TransactionAttempt = { 
    Deposited:float
    Requires:float 
}

type RequestResult =
    | Denied of TransactionAttempt
    | Granted of Product

В моем тесте я хочу сделать это:

let display = balance |> select Pepsi
display |> should equal Denied

Я не против сделать это:

display |> should equal (Denied _)

Тем не менее, я вынужден сделать это:

display |> should equal (Denied {Deposited=0.25; Requires=1.00})

Обратите внимание, как явно я должен быть с выражением выше.

Следовательно, я просто хочу знать, если это было отказано. Меня не волнуют детали.

Вот фактический тест:

[<Test>]
let ``Vending machine reflects more money required for selection``() =
   // Setup
   let balance = Quarter |> insert []

   // Test
   let display = balance |> select Pepsi

   // Verify
   display |> should equal (Denied {Deposited=0.25; Requires=1.00})

Вот функция:

let select product balance =
    let attempt = { Deposited=balance
                    Requires=product |> getPrice }

    let paidInFull = attempt.Deposited >= attempt.Requires

    if not paidInFull then 
        Denied attempt
    else Granted product

Вот весь домен:

module Machine

type Deposit =
    | Nickel
    | Dime
    | Quarter
    | OneDollarBill
    | FiveDollarBill

type TransactionAttempt = { 
    Deposited:float
    Requires:float 
}

type State =
    | OutOfService
    | PaymentReceived of Deposit
    | WaitingForSelection
    | NotPaidInFull of TransactionAttempt

type Product =
    | Pepsi
    | Coke
    | Sprite
    | MountainDew

type RequestResult =
    | Denied of TransactionAttempt
    | Granted of Product

(* Functions *)
open System

let display = function
    | OutOfService            -> "Out of Service"
    | WaitingForSelection     -> "Make selection"
    | NotPaidInFull attempt   -> sprintf "%s Required" ((attempt.Requires - attempt.Deposited).ToString("C2"))
    | PaymentReceived deposit -> match deposit with
                                 | Nickel         -> "5¢"
                                 | Dime           -> "10¢"
                                 | Quarter        -> "25¢"
                                 | OneDollarBill  -> "$1.00"
                                 | FiveDollarBill -> "$5.00"

let getBalance coins =
    coins |> List.fold (fun acc d -> match d with
                                     | Nickel         -> acc + 0.05
                                     | Dime           -> acc + 0.10
                                     | Quarter        -> acc + 0.25
                                     | OneDollarBill  -> acc + 1.00
                                     | FiveDollarBill -> acc + 5.00) 0.00
let insert balance coin =
    coin::balance |> getBalance

let getPrice = function
    | Pepsi       -> 1.00
    | Coke        -> 1.00
    | Sprite      -> 1.00
    | MountainDew -> 1.00

let select product balance =
    let attempt = { Deposited=balance
                    Requires=product |> getPrice }

    let paidInFull = attempt.Deposited >= attempt.Requires

    if not paidInFull then 
        Denied attempt
    else Granted product

1 ответ

Решение

Самое простое, что я могу придумать, это написать предикат для проверки, которую вы хотите:

let isDenied du =
    match du with
    | Denied _ -> true
    | _ -> false

Или так как let f x = match x with ... эквивалентно let f = function ..., это может быть:

let isDenied = function Denied _ -> true | _ -> false

Тогда ваш тест выглядит так:

display |> isDenied |> should be True

Обратите внимание, что Trueс большой буквы T является ограничением. Если вы сравниваете с логическим значением, то это будет

display |> isDenied |> should equal true

Если вы обнаружите, что вам нужно написать МНОЖЕСТВО этих пользовательских предикатов, возможно, существует более общее решение, включающее написание пользовательских ограничений для NUnit или XUnit или любой другой инфраструктуры тестирования, которую вы используете в серверной части. Но, как относительный новичок в F#, вам, вероятно, следует сначала воспользоваться простым решением, а затем обобщить его.

PS Если вы решили написать собственное ограничение, посмотрите https://github.com/fsprojects/FsUnit/blob/master/src/FsUnit.NUnit/FsUnit.fs (если вы используете NUnit) или https://github.com/fsprojects/FsUnit/blob/master/src/FsUnit.Xunit/FsUnit.fs (если вы используете XUnit) для вдохновения.

Другие вопросы по тегам