Mavnn's blog

Stuff from my brain

Going Down the Property Based Testing Rabbit Hole

Image, if you will, a card game.

(Don't worry, there's code later. Lots of code.)

It's not a complex card game; it's a quick and fun game designed to represent over the top martial arts combat in the style of Hong Kong cinema or a beat 'em up game.

Each player has a deck of cards which represent their martial art; different arts are differently weighted in their card distribution. These cards come in four main types:

1 Normal cards

A "normal" card comes in one of four suits:

  • Punch
  • Kick
  • Throw
  • Defend

They also carry a numerical value between 1 and 10, which represents both how "fast" they are and (except for defend cards) how much damage they do. A Defend card can never determine damage.

2 Special Attack cards

The fireballs, whirling hurricane kicks and mighty mega throws of the game. A special attack card lists two suits: one to use for the speed of the final attack, and one for the damage. This allows you to play 3 cards together to create an attack which is fast yet damaging.

3 Combo Attack cards

A flurry of blows! Combo cards also list two suits: one for speed, and one for the "follow up" flurry. This allows you to play 3 cards together, one of which determines the speed of the attack while the other adds to the total damage. For example, if you play a Punch/Kick Combo with a Punch 3 and a Kick 7 you end up with a speed 3, damage 10 attack.

4 Knockdown cards

You can combine a knockdown card with any other valid play to create an action that will "knockdown" your opponent.

The code

(This is an example of property based testing; if you need an introduction first, check out Breaking Your Code in New and Exciting Ways or the the video version)

There are of course other rules to the game; but let's assume for a moment we're coding this game up in F#. We've defined a nice domain model:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
module BlackBelt.Domain.Types

open System

type Suit =
    | Punch
    | Kick
    | Throw
    | Defend

type NormalCard =
    { Suit : Suit
      Value : int }

type ComboCard =
    { SpeedSuit : Suit
      FollowUpSuit : Suit }

type SpecialAttackCard =
    { SpeedSuit : Suit
      DamageSuit : Suit }

type Card =
    | Normal of NormalCard
    | Combo of ComboCard
    | Special of SpecialAttackCard
    | Knockdown

type Action =
    { Speed : int
      Damage : int
      Suit : Suit
      Knockdown : bool }

type PlayerId = PlayerId of string

type Player =
    { Name : string
      Id : PlayerId
      Deck : Card list
      Stance : Card list
      Health : int }

type WaitingFor =
    | Attack
    | Counter of PlayerId * Action
    | StanceCard

type Game =
    { GameId : Guid
      Player1 : Player
      Player2 : Player
      TurnOf : PlayerId
      WaitingFor : WaitingFor }

And now we want to write a function that takes the rules for playing cards above, and turns a Card list into an Action option (telling you if the list is a valid play, and what action will result if it is).

This function is pretty critical to the overall game play, and may well also be used for validating input in the UI so getting it right will make a big difference to the experience of playing the game.

So we're going to property test our implementation in every which way we can think of…

First step: make yourself a placeholder version of the function to reference in your tests:

1
2
3
module BlackBelt.Domain.Logic

let toAction cards = None

Now, let's start adding properties. All of the rest goes in a single file, but I'm going to split it up with some commentary as we go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
module BlackBelt.Tests.Logic

open Expecto
open Expecto.ExpectoFsCheck
open FsCheck
open BlackBelt.Domain.Logic
open BlackBelt.Domain.Types

let allSuitsBut suit =
    [Punch;Kick;Throw;Defend]
    |> List.filter ((<>) suit)
    |> Gen.elements

// We need a custom generator here as only some
// values are valid
type DomainArbs() =
    static member NormalCard() : Arbitrary<NormalCard> =
        gen {
            let! suit = Arb.generate<Suit>
            let! value = Gen.choose(1, 10)
            return
                { Suit = suit
                  Value = value }
        } |> Arb.fromGen

    static member SpecialAttackCard() : Arbitrary<SpecialAttackCard> =
        gen {
            let! damageSuit = allSuitsBut Defend
            let! speedSuit = Arb.generate<Suit>
            return
                { SpeedSuit = speedSuit
                  DamageSuit = damageSuit }
        } |> Arb.fromGen

    static member ComboCard() : Arbitrary<ComboCard> =
        gen {
            let! speedSuit = allSuitsBut Defend
            let! followupSuit = allSuitsBut Defend
            return
                { SpeedSuit = speedSuit
                  FollowUpSuit = followupSuit }
        } |> Arb.fromGen

We'll start off with a few general purpose bits for generating random types in our domain. I haven't gone the whole hog in making illegal states unrepresentable here, so we need to constrain a few things (like the fact that cards only have values from 1 to 10, and that you can't combo into a defend card for extra damage).

Now: let's start generating potential plays of cards. Our properties will be interested in whether a particular play is valid or invalid, and we will want to know what the resulting Action should be for valid plays.

So we define a union to create instances of:

1
2
3
type GeneratedAction =
    | ValidAction of Card list * Action
    | InvalidAction of Card list

Now let's add all of the valid actions we can think of.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let makeNormalAction =
    gen {
        let! normal = Arb.generate<NormalCard>
        let cards = [Normal normal]
        let action =
            { Speed = normal.Value
              Damage =
                  if normal.Suit = Defend then
                      0
                  else
                      normal.Value
              Suit = normal.Suit
              Knockdown = false }
        return cards, action
    }

So; a normal card on it's own is always a valid play, the only thing we need to watch out for is that a Defend card causes no damage.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let makeComboAttack =
    gen {
        let! comboCard = Arb.generate<ComboCard>
        let! normal1 = Arb.generate<NormalCard>
        let! normal2 = Arb.generate<NormalCard>
        let cards =
            [ Normal { normal1 with Suit = comboCard.SpeedSuit }
              Normal { normal2 with Suit = comboCard.FollowUpSuit }
              Combo comboCard ]
        let attack =
            { Speed =
                  if comboCard.SpeedSuit = comboCard.FollowUpSuit then
                      min normal1.Value normal2.Value
                  else
                      normal1.Value
              Damage = normal1.Value + normal2.Value
              Suit = comboCard.SpeedSuit
              Knockdown = false }
        return cards, attack
    }

Here we'll generate the combo card and two other cards, and then we'll override the suit of the two normal cards to ensure they're legal to be played with the combo card.

There's a quirk here (which in reality I noticed after trying to run these tests). If the two suits are the same, the fast card should determine the speed regardless of "order".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let makeSpecialAttack =
    gen {
        let! specialCard = Arb.generate<SpecialAttackCard>
        let! damageValue = Gen.choose(2, 10)
        let! speedValue = Gen.choose(1, damageValue - 1)
        let cards =
            [ Normal { Suit = specialCard.SpeedSuit; Value = speedValue }
              Normal { Suit = specialCard.DamageSuit; Value = damageValue }
              Special specialCard ]
        let attack =
            { Speed = speedValue
              Damage = damageValue
              Suit = specialCard.SpeedSuit
              Knockdown = false }
        return cards, attack
    }

Special attack cards have an additional constraint: playing a high value speed card with a low value damage card would actually disadvantage the player, and so is not considered a valid play.

1
2
3
4
5
6
7
8
9
10
let makeKnockdownAttack =
    gen {
        let! cards, baseAttack =
            Gen.oneof [ makeNormalAction
                        makeComboAttack
                        makeSpecialAttack ]
        let cards = Knockdown::cards
        let attack = { baseAttack with Knockdown = true }
        return cards, attack
    }

Here we make use of the generators we've constructed above to create a Knockdown action.

1
2
3
4
5
6
7
8
9
let makeValidAction =
    gen {
        let! validAction =
            Gen.oneof [ makeNormalAction
                        makeComboAttack
                        makeSpecialAttack
                        makeKnockdownAttack ]
        return ValidAction validAction
    }

Which allows us to write a ValidAction generator.

Now, more interesting is trying to generate plays which are not valid. We're not trusting the UI to do any validation here, so let's just come up with everything we can think of…

1
2
3
4
5
6
let multipleNormal =
    gen {
       let! first = Arb.generate<NormalCard>
       let! normals = Gen.nonEmptyListOf Arb.generate<NormalCard>
       return first::normals |> List.map Normal
    }

More than one normal card with out another card to combine them is out.

1
2
3
4
5
6
7
8
let incompleteComboOrSpecial =
    gen {
        let! special =
            Gen.oneof [ Gen.map Combo Arb.generate<ComboCard>
                        Gen.map Special Arb.generate<SpecialAttackCard> ]
        let! other = Arb.generate<Card>
        return [special; other]
    }

A combo or special card always requires precisely two normal cards to be a valid play; so here, we only generate one.

1
2
let onlyKnockdown =
    Gen.constant [Knockdown]

A combo card can only be played as part of an otherwise valid play, and isn't allowed on it's own.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
let unmatchedSpeedCombo =
    gen {
        let! combo = Arb.generate<ComboCard>
        let! normal1 = Arb.generate<NormalCard>
        let! normal2 = Arb.generate<NormalCard>
        let! unmatched1 = allSuitsBut combo.SpeedSuit
        let! unmatched2 = allSuitsBut combo.SpeedSuit
        let n1 = { normal1 with Suit = unmatched1 }
        let n2 = { normal2 with Suit = unmatched2 }
        return [Combo combo; Normal n1; Normal n2]
    }

let unmatchedSpeedSpecial =
    gen {
        let! special = Arb.generate<SpecialAttackCard>
        let! normal1 = Arb.generate<NormalCard>
        let! normal2 = Arb.generate<NormalCard>
        let! unmatched1 = allSuitsBut special.SpeedSuit
        let! unmatched2 = allSuitsBut special.SpeedSuit
        let n1 = { normal1 with Suit = unmatched1 }
        let n2 = { normal2 with Suit = unmatched2 }
        return [Special special; Normal n1; Normal n2]
    }

let unmatchedDamageCombo =
    gen {
        let! combo = Arb.generate<ComboCard>
        let! normal1 = Arb.generate<NormalCard>
        let! normal2 = Arb.generate<NormalCard>
        let! unmatched1 = allSuitsBut combo.FollowUpSuit
        let! unmatched2 = allSuitsBut combo.FollowUpSuit
        let n1 = { normal1 with Suit = unmatched1 }
        let n2 = { normal2 with Suit = unmatched2 }
        return [Combo combo; Normal n1; Normal n2]
    }

let unmatchedDamageSpecial =
    gen {
        let! special = Arb.generate<SpecialAttackCard>
        let! normal1 = Arb.generate<NormalCard>
        let! normal2 = Arb.generate<NormalCard>
        let! unmatched1 = allSuitsBut special.DamageSuit
        let! unmatched2 = allSuitsBut special.DamageSuit
        let n1 = { normal1 with Suit = unmatched1 }
        let n2 = { normal2 with Suit = unmatched2 }
        return [Special special; Normal n1; Normal n2]
    }

There's lots of ways to combine three cards which are not valid combos or specials. Here we use are allSuitsBut helper function to always play just the wrong cards compared to what's needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let swappedSpecial =
    gen {
        let! specialCard = Arb.generate<SpecialAttackCard>
        if specialCard.SpeedSuit = specialCard.DamageSuit then
            return [Special specialCard]
        else
            let! speedValue = Gen.choose(2, 10)
            let! damageValue = Gen.choose(1, speedValue)
            let cards =
                [ Normal { Suit = specialCard.SpeedSuit; Value = speedValue }
                  Normal { Suit = specialCard.DamageSuit; Value = damageValue }
                  Special specialCard ]
            return cards
    }

And here we create special attacks which are slower than they are damaging. If the speed and damage suit are the same, the cards could be used either way around to create a valid action, so instead we just return the Special card on it's own without companions to form a different invalid play.

1
2
3
4
5
6
7
8
9
10
11
12
13
let makeInvalidAction =
    gen {
        let! invalidAction =
            Gen.oneof [ multipleNormal
                        incompleteComboOrSpecial
                        onlyKnockdown
                        unmatchedSpeedCombo
                        unmatchedSpeedSpecial
                        unmatchedDamageCombo
                        unmatchedDamageSpecial
                        swappedSpecial ]
        return InvalidAction invalidAction
    }

There's more that could be added here, but I decided that was enough to keep me going for the moment and so added my invalid action generator here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type ActionArbs() =
    static member GeneratedAction() : Arbitrary<GeneratedAction> =
        gen {
            return! Gen.oneof [
                        makeValidAction
                        makeInvalidAction
                    ]

        } |> Arb.fromGen

let actionConfig =
    { FsCheckConfig.defaultConfig with
        arbitrary = [typeof<DomainArbs>
                     typeof<ActionArbs>] }
[<Tests>]
let toAction =
    testPropertyWithConfig actionConfig "toAction function" <| fun action ->
        match action with
        | ValidAction (cards, action) ->
            Expect.equal (toAction cards) (Some action) "Is an action"
        | InvalidAction cards ->
            Expect.isNone (toAction cards) "Is not an attack"

Finally, I wired up the generators and defined the single property this function should obey: it should return the correct action for a valid play, or None if the play is erroneous.

The wrap

Hopefully this is a useful example for those of you using property based tests of how you can encode business logic into them: although this looks like a lot of code, creating even single examples of each of these cases would have been nearly as long and far less effective in testing.

It does tend to lead to a rather iterative approach to development, where as your code starts working for some of the use cases, you begin to notice errors in or missing cases you need to generate, which helps you find more edges cases in your code and round the circle you go again.

If you want, you're very welcome to take this code to use as a coding Kata - but be warned, it's not as simple a challenge as you might expect from the few paragraphs at the top of the post!

All Saints’ Day Sale

TL;DR: 10% off Building Solid Systems in F# until 7th November 2017

Lots of people these days seem to like giving Halloween sales, but historically and theologically, Halloween is really just the precursor to the real celebration: All Saints' Day.

So in the interest of getting the details right, we're having an All Saints' Day sale, starting today for 7 days. It's already live, get your 10% off your tickets now.

RouteMaster : Master Your Messaging Routes

I'm very pleased to announce the release of an initial alpha of RouteMaster.

What is it? Well, I'll let the README speak for itself:

RouteMaster is a .NET library for writing stateful workflows on top of a message bus. It exists to make the implementation of long running business processes easy in event driven systems.

There is also example code in the repository so you can see what things are starting to look like.

For those of you following along, this will sound awfully familiar; that's because RouteMaster is the outcome of my decision to rebuild a Process Manager for EasyNetQ. The first cut of that was imaginatively called "EasyNetQ.ProcessManager", but I decided to rename it for three main reasons:

  • On re-reading Enterprise Integration Patterns, it occurred to me that RouteMaster was an enabler for many of the other patterns as well as the "Process Manager"
  • The message bus RouteMaster uses is provided as an interface; the main dll has no dependency on EasyNetQ at all
  • The previous EasyNetQ.ProcessManager is still available as a Nuget package supplied by my previous employer, and they have both the moral and legal rights to the package given I wrote the original on their time

A pre-emptive few FAQs:

Is this ready to use?

No, not yet. I'm out of time I can afford to spend on it right now, get in touch if you can/want to fund future development.

If you want to play, the code as provided does run and all of the process tests pass.

Urgh! All the examples are F#!?

Yes, but there is a C# friendly API in the works. See the first question :)

What infrastructure do I need to run this?

At the moment, I'm using EasyNetQ (over RabbitMQ) and PostgreSQL (via Marten) for transport and storage respectively.

What about things like NServiceBus and MassTransit?

In some ways they fall in a similar space to RouteMaster, but with a different philosophy. Just as EasyNetQ is a focused library that supplies only part of the functionality you'd find in these larger solutions, RouteMaster is designed to work with your chosen transport abstraction not replace it.

Ask not what your RouteMaster can do for you, but what you can do for your RouteMaster!

I'd really like feedback, ideas, use cases and suggestions - leave comments here or ping an issue onto the repository. If you're feeling really brave and can try and actually experiment with it, but at the moment I'm mostly hoping for concrete use cases and, well, funding.

Quite a few people over the years have hit my website searching for an EasyNetQ process manager, and others have asked me if it's still available. I'd like to hear from as many of you as possible to build the tightest, simplest solution which will do the job.

POSTing to Freya

I've written about how nice Freya is as a library, but documentation is still a little light on the ground.

So here's a minimal implementation of a "microservice" Freya API, starting from which dotnet commands to run to install the Freya template, through to a running web service.

Make sure you have an up to date .NET Core SDK installed, and grab yourself the handy dandy Freya template:

1
dotnet new -i "Freya.Template::*"

Then create yourself a directory and go into it. The following command will set up a brand new Freya project using kestrel as the underlying webserver, and Hopac (rather than F# Async) for concurrency. Alternatively, you can leave both the options off and you'll get Freya running on Suave with standard Async.

1
dotnet new freya --framework kestrel --concurrency hopac

Your project should run at this point; dotnet run will spin up a webserver on port 5000 which will give a 404 at the root and text responses on /hello and /hello/name paths.

Api.fs is where all the magic of configuring Freya happens - KestrelInterop.fs contains boilerplate for making sure Routing information passes correctly between Kestrel and Freya, and Program.fs just starts Kestrel with our Freya API as an OWIN app.

Adding JSON

So, this is great and all, but we're building a microservice aren't we? That normally means JSON (or at least something more structured than plain text!).

Let's change things up so that as well as supplying the name to greet in the route, we can POST JSON with a name field to the /hello end point.

To respond in JSON, we need a Freya Represent record. We're sending a result with a fixed structure, so we don't need a serialization library or anything, we'll just construct the JSON by hand. Stick this near the top of Api.fs:

Workflow Alpha

Log of workflow test running

It's alive! The process manager code I've been reconstructing (see Intro and the in memory test bus) is slowly starting to take some shape.

As you can see, it comes with nice (no dependency) logging out of the box and it is async all the way down to the underlying transport.

This is still at the underlying plumbing phase in many ways: the code to construct a workflow like this is currently a boilerplate covered ugly mess - but it's all boilerplate which has been deliberately designed to allow powerful APIs to be built over the top.

Next up: a nice sleek API for creating "pipeline" workflows more easily. Then the real fun starts - pleasant to use abstractions over fork/join semantics…

Interested in seeing faster progress on this project? Drop [email protected] a line to talk sponsorship.

Kubernetes for the Masses

As part of a two day training course I'm going to be putting together a bunch of material on how to run .NET Core code on kubernetes.

It will include things like:

  • Setting up CI/automated builds
  • Configuring ingress points (including ssl via self updating Let's Encrypt certs)
  • Monitoring and metrics of the running system

If you'd have any interest in this material as a separate module (which wouldn't be F# specific), do get in touch. I'd reckon it will become a one day course, either in house or hosted.

As an aside, once the material has been battle tested a few times, I will also be putting together a "base" deployment repository - although it will have fair warning that Kubernetes is really still the kind of thing that requires you to understand it before you start pushing things onto it, not just cutting and pasting someone else's config.

After all, you're the one who's going to get the phone call at 03:00…

Functional Programming in the Wild

Last week was the annual Progressive .Net conference, hosted at SkillsMatter.

It was a bit of a strange conference for me; I was invited to speak, and very much enjoyed meeting up with everyone but I also had a bunch of other work I needed to get done. That means that unlike some other conferences I won't be able to give much speaker feedback!

However, there were a few sessions I managed to pay some attention to and some thoughts about my own that I'd like to record - so here goes!

Building Solid Systems in F#

We are running another course, and I'm officially stoked! Read on for the details…

Building Solid Systems in F#

Writing code is only the first part of putting software into production - to run, maintain and scale your product, you'll need to understand and design the overall system.

This 2 day course will take you through best practice in both writing an F# based, distributed system and running it in production - including sensible inter-operation with components written in other languages.

To fit this into 2 days, we'll need to make some opinionated decisions (such as using F#!), but many of the ideas and concepts will be transferable.

We'll cover:

  • Writing reliable code using F#’s unique language features
  • Learn how to apply SOLID (like) principles in a functional style
  • How to instrument distributed services
  • Continuous Improvement:
    • Unit testing
    • Performance measurement
  • Running distributed systems in development
  • Deploying distributed systems to production
  • Good practice in dependency management and code organization

You'll come away with:

A git repository of your completed work, which will include:

  • Nicely instrumented, benchmarked and unit tested F# services
  • A scripted, deterministic deployment process for the overall distributed system
  • Real time centralized logging, metrics and health feedback from the system, whether running on the dev machine or in production
  • Zero down time continuous deployment for the overall system

Prerequisites:

You need to have:

  • a basic knowledge of F# syntax
  • a reasonable background knowledge of software development

You need to bring laptop with:

  • a relatively recent F# development environment (you’ll need to be able to build dotnet core 2.0 apps)
  • Minikube installed

We’ll also ask you to pre-download some code and containers in advance so that we can hit the ground running on the first day; we’ll pass you the details of that before the event.

Is this a replacement for Level Up Your F#?

No; Level Up Your F# focussed on the details of the F# language, this course is focussed on building systems. We will be running Level Up Your F# again in the future.

Where will it happen?

At the Wellcome Collection

Where can I get tickets?

Right here!

(Or go to EventBrite if that form isn't working for you)

What have other people said about your courses?

Hassan Ezzahir, Lead developer (Contractor) at BNP Paribas

I’ve been trying to learn F# for several years now and got almost all the existing books on the subjects.

Yet, I felt there was a gap between my good understanding of the language and actually applying it on bigger “real” projects.

Michael’s great training skills has enabled me to quickly practice some advanced topics I was less familiar with.

With my newly acquired knowledge, I’m confident I will be able achieve some great (and fun) developments with F#

Alexander Battisti, Senior Software Developer (Machine Learning) at Freeletics GmbH

I found "Level Up your F#" a good course teaching advanced topics in F# that are commonly ignored or glossed over when you are on your own, but are useful enough so you shouldn't ignore them. The exercises were difficult in a good way and engaging. I definitely recommend the course for anybody after they took their first serious steps in learning F#.

System.Console Is Why We Can’t Have Nice Things

In writing a simple tutorial for this years Progressive .Net I thought I'd use the Console to allow some nice visual feedback with requiring a dependency.

TD;LR: System.Console (at least on dotnet core 2.0) is not as threadsafe as you'd hope, and means that writing any simple cross platform console UI is nearly impossible.

An in Memory Message Bus in 100 Lines or Less

In reimplementing an EasyNetQ process manager one of the things I wanted to keep from the original project was an in memory message bus that could be used for testing without requiring a running RabbitMQ server. The code has ended up being pleasingly short and also uses a few techniques that seemed interesting, so I thought I'd document it here as part of the design process.

Please note we're not going for a full re-implementation of RabbitMQ in memory here, but this does give us enough to do some useful testing!