Last active
February 17, 2017 11:59
-
-
Save Porges/c7a6c6abf5e22b1e65a7445e8ff47ce0 to your computer and use it in GitHub Desktop.
Code for "Model based testing with F# & FsCheck"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[<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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Operation = | |
| Add of User | |
| Get of UserId | |
| Delete of UserId |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = | |
// ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[<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