Search code examples
f#.net-coreexpecto

How to assert an exception is expected


I'm on a Mac running F# using .NET Core 2.0.

I have a function that looks like this:

let rec evaluate(x: string) =
  match x with
  // ... cases
  | _ -> failwith "illogical"

I'd like to write an Expecto test that validates that the exception is thrown as expected, something along the lines of:

// doesn't compile
testCase "non-logic" <| fun _ ->
  Expect.throws (evaluate "Kirkspeak") "illogical" 

The error is

This expression was expected to have type 'unit -> unit' but here has type 'char'

unit -> unit makes me this is analogous to Assert.Fail, which is not what I want.

Being somewhat new to F# and Expecto, I'm having trouble locating a working example of asserting that an exception is thrown as expected. Does anyone have one?


Solution

  • Expect.throws has the signature (unit -> unit) -> string -> unit so the function you want to test must be (unit -> unit) or be wrapped inside a function that is (unit -> unit).

    let rec evaluate (x: string) : char =
      match x with
      // ... cases
      | _ -> failwith "illogical"
    

    The compiler error is telling you that the function you passed to Expect.throws does not have the right signature yet.

    [<Tests>]
    let tests = testList "samples" [
        test "non-logic" {
          // (evaluate "Kirkspeak") is (string -> char)
          // but expecto wants (unit -> unit)
          Expect.throws (evaluate "Kirkspeak") "illogical"
        }
    ]
    
    [<EntryPoint>]
    let main argv =
        Tests.runTestsInAssembly defaultConfig argv
    

    One way to make it work is to change

    Expect.throws (evaluate "Kirkspeak") "illogical"
    

    to

    // you could instead do (fun () -> ...)
    // but one use of _ as a parameter is for when you don't care about the argument
    // the compiler will infer _ to be unit
    Expect.throws (fun _ -> evaluate "Kirkspeak" |> ignore) "illogical"
    

    Now expecto is happy!

    expecto says the test asserting an exception throws passes

    This answer was the way I thought through it. It is usually helpful to follow the type signatures.

    EDIT: I saw your error message saying This expression was expected to have type 'unit -> unit' but here has type 'char' so I updated my answer to match it.