Skip to content

Instantly share code, notes, and snippets.

@Porges
Last active February 17, 2017 11:59
Show Gist options
  • Save Porges/c7a6c6abf5e22b1e65a7445e8ff47ce0 to your computer and use it in GitHub Desktop.
Save Porges/c7a6c6abf5e22b1e65a7445e8ff47ce0 to your computer and use it in GitHub Desktop.
Code for "Model based testing with F# & FsCheck"
[<Struct>]
type UserId = UserId of string
type User = { id : UserId ; name : string ; age : int }
type IUserOperations =
abstract member AddUser : User -> unit
abstract member GetUser : UserId -> Option<User>
abstract member DeleteUser : UserId -> unit
open System.Collections.Generic
type UserSystem() =
let users = Dictionary<UserId, User>()
// Exposed for us to use later:
member this.UserCount = users.Count
// And implement the interface:
interface IUserOperations with
member this.AddUser u =
users.[u.id] <- u
member this.GetUser id =
match users.TryGetValue id with
| (true, user) -> Some user
| _ -> None
member this.DeleteUser (UserId id) =
if id.Contains "*" // Uh-oh: catastrophic bug!
then users.Clear()
else users.Remove (UserId id) |> ignore
type Operation =
| Add of User
| Get of UserId
| Delete of UserId
let applyOp (op : Operation) (handler : IUserOperations) =
match op with
| Add user -> handler.AddUser user
| Get name -> handler.GetUser name |> ignore
| Delete name -> handler.DeleteUser name
// this is our model
// the only thing we care about is how many users there are
// so we just store the names as a set
type UserCountModel() =
let users = HashSet<UserId>()
member this.Verify (real : UserSystem) =
Xunit.Assert.Equal(users.Count, real.UserCount)
interface IUserOperations with
member this.AddUser u =
users.Add u.id |> ignore
member this.DeleteUser name =
users.Remove name |> ignore
member this.GetUser name = None
open FsCheck
open FsCheck.Xunit
type Arbs =
// declare we only want non-null strings
static member strings() =
Arb.filter (fun s -> s <> null) <| Arb.Default.String()
[<Properties(Arbitrary=[|typeof<Arbs>|])>]
module Tests =
// ...
[<Property>]
let ``Check implementation against model`` operations =
// create both implementations empty
let real = UserSystem()
let model = UserCountModel()
// apply all the operations
let applyToBothModels op =
applyOp op real
applyOp op model
List.iter applyToBothModels operations
// verify the result
model.Verify real
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment