Mohsen
Mohsen

Reputation: 4266

How to generate tuples by FsCheck

This is a json generation :

let strGen = Arb.Default.String()
                |> Arb.toGen
strGen
    |> Gen.arrayOf
    |> Gen.map (String.concat "\", \"")
    |> Gen.map (fun strs -> "[\"" + strs + "\"]")

How can I have the string that the json have been created from in my test body to assert the final result.

Upvotes: 3

Views: 366

Answers (2)

rmunn
rmunn

Reputation: 36688

My original answer was to use Gen.map2 to combine two generators, one for the string array and one for the json string. But Gen.map2 is specifically designed to let two independent generators be combined, i.e., the result of one generator won't affect the result of the other one. (E.g., rolling two dice: the result of the first die is independent of the result of the second die). What you need is a simple Gen.map that takes the string array generator and produces a tuple of (string array, json). Like so:

let strGen = Arb.Default.String() |> Arb.toGen
let arrayGen = strGen |> Gen.arrayOf
arrayGen |> Gen.map (fun array ->
    let json =
        array
        |> String.concat "\", \""
        |> fun strs -> "[\"" + strs + "\"]")
    array,json)

Unlike my answer below which combined two independent generators, here there is only ONE generator, whose value is used to produce both the array and the json values. So these values will be dependent rather than independent, and the json will always match the string array.

Original, INCORRECT, answer below, preserved in case the contrast between the two answers is useful:

Easy. Just save the array generator, and re-use it later, using Gen.map2 to combine the array and the json. E.g.:

let strGen = Arb.Default.String()
                |> Arb.toGen
let arrayGen = strGen |> Gen.arrayOf
let jsonGen =
    arrayGen
    |> Gen.map (String.concat "\", \"")
    |> Gen.map (fun strs -> "[\"" + strs + "\"]")
Gen.map2 (fun array json -> array,json) arrayGen jsonGen

And now you have a generator that produces a 2-tuple. The first element of the tuple is the string array, and the second element is the json that was generated.

BTW, your JSON-creating code isn't quite correct yet, because if the generated string contains quotation marks, you'll need to quote them in some way or your generated JSON will be invalid. But I'll let you handle that, or ask a new question about that if you don't know how to handle that. The "single responsibility principle" applies to Stack Overflow questions, too: each question should ideally be about just one subject.

Upvotes: 4

Kurt Schelfthout
Kurt Schelfthout

Reputation: 8990

Can't seem to be able to put the code in comments, so here's a cleaned up version:

let isDigitOrWord i =
        i |> String.isNullOrEmpty 
        |> not && Regex.IsMatch(i,"^[a-zA-Z0-9 ]*$")

let strGen = Arb.Default.String() |> Arb.toGen

Gen.arrayOf strGen 
|> Gen.map (fun array ->
    let array = array |>  Array.filter isDigitOrWord
    let json =
        array
        |> String.concat "\", \"" 
        |> fun strs -> if strs|> String.isEmpty then strs else "\"" + strs + "\""
        |> fun strs -> "[" + strs + "]"
    array,json)

Upvotes: 2

Related Questions