bookofproofs
bookofproofs

Reputation: 354

Unit Testing Parsers Written Using FParsec

I'd like to write unit tests for abstract syntax trees generated by a parser that I write using FParsec.

For instance, given the parser

 open FParsec

 type FooValue = FooValue of int
 let foo: Parser<string,unit> = pstring "foo" 
 let fooValue = pint32
 let p = foo >>. spaces >>. fooValue |>> FooValue.FooValue
 

the only testing way I found so far was writing

let resultSuccess = run p "foo 23"
let resultFailure = run p "foo noNumber"

printfn "%O" resultSuccess
printfn "%O" resultFailure

and checking if my F# program outputs

Success: FooValue 23
Failure:
Error in Ln: 1 Col: 5
foo noNumber
    ^
Expecting: integer number (32-bit, signed)

Instead, I'd prefer to write some unit tests like these:

open FParsec
open Microsoft.VisualStudio.TestTools.UnitTesting

[<TestClass>]
type TestMyParser () =

    [<TestMethod>]
    member this.TestPSucceeds () =
        let actual = run p "foo 23"
        let expected = ......? 
        Assert.AreEqual(actual, expected);

    [<TestMethod>]
    member this.TestPFails () =
        let actual = run p "foo noNumber"
        let expected = ......? 
        Assert.AreEqual(actual, expected);

As you can see, I have no clue how to construct valid functions for expected values of the abstract syntax trees of FParsec so I could somehow compare them with the actual result of each test I run.

Edit: It would help me also (and probably simplify the comparison of more complex ASTs a lot) if I could somehow serialize the actual results to strings and just compare them to expected strings.

Upvotes: 1

Views: 34

Answers (1)

bookofproofs
bookofproofs

Reputation: 354

I found out how to do it, it is relatively simple using sprintf. The TestMethod could look like this:

    [<TestMethod>]
    member this.TestPSucceeds () =
        let result = run p "foo 23"
        let actual = sprintf "%O" result
        let expected = """Success: FooValue 23""".Replace("\r","")
        Assert.AreEqual(expected, actual);

Enclosing the expected string in """, followed by the Replace("\r","") function makes sure that you can also paste more complex, multi-line parser output here.

Upvotes: 0

Related Questions