Category Archives: Uncategorized

Early Fable Adventures – Building A Memory Tiles Game

Getting aquianted with fable

Just about a month ago, I had the great pleasure of attending F# eXchange 2017 in London. It was 2 days of wonderful F# indulgence with fantastic talks and workshops. It was also a great experience meeting some of the people who have pretty much been virtual mentors to me on my F# journey like Kit Eason and Scott Wlaschin. I did Kit’s pluralsight F# courses and Scott’s fsharp for fun and profit site is a constant source of learning for me so to get chatting with these guys in person was really cool.

Among all of these great talks and workshops was a talk by Alfonso García-Caro, about his Fable compiler. This is an F# to javascript compiler that hooks in really well with the javascript ecosystem. I’m lucky that in my job I am coding in F# across the full stack currently using websharper for our F# to javascript needs. So Alfonso’s talk about Fable really caught my attention. I couldn’t wait to get a bit of free time to dive in and try it out.

Getting up and running with Fable was very quick and easy thanks to this extremely helpful blog post by Krzysztof Cieslak. I started by simply trying out simple dom event handling. Out of the box, Fable has an API that appears to be a light F# wrapper around the javascript dom API.
This is accessed simply with

open Fable.Import

From here, the dom is at your finger tips through the eyes of F# through the module Fable.Import.Browser.

A little game to get my teeth into Fable

After playing around with some dom operations through Fable, I wanted something a bit more substantial to get my teeth into. Recently I was playing this common memory game with my 3 year old daughter. Its a game where you have a grid of cards with pictures on them where every picture will appear on exactly 2 cards. You lay them out randomly upside down. Then you take turns turning 2 cards over at a time. If you get matching pictures, you keep the cards but if there is no match, you turn the cards upside down again. At the end, the player with the most mathed cards wins.

I thought this would be a great game to implement with Fable so I could get started properly with this wonderful F# to javascript compiler. I simplified the game for my first version and built just a one player game. You get a grid of blacked out tiles which have hidden colors where every color will exist twice on the grid. Then you click (or tap if on a mobile device) on 2 tiles to uncover their colors. If they match, the tiles stay open permently until you start a fresh game. If they are not a match, the tiles are blacked out again the next time you click a new tile. This first version was enough to get me building something with Fable. In future versions, I plan to make it a bit more interesting by adding time challenges and levels with bigger grids among other ideas.

Dom operations through Fable to get up and running

There are a few basic things that I wanted to do with the dom for my game interaction. The following were enough for the first version of the game.

Creating a dom element with attributes

let tileDiv = Browser.document.createElement("div")
             tileDiv.className <- "col-3"
             let bc = match tile.Status with
                      | UnMatched -> tile.CoverColor
                      | AttemptingMatch -> tile.HiddenColor
                      | Matched -> tile.HiddenColor
             tileDiv.style.backgroundColor <- bc
             tileDiv.style.border <- "white solid 4px"

In the above, the Browser module is coming from the earlier Fable.Import. From this module, there is a wealth of dom related functions closely matching the standard javascript document API. Here I’m creating a div, setting its class name and a couple other properties including its border and also its background color which is a result of a pattern match to check the status of the corresponding tile in the backing model.

Adding a click event

Adding events is straight forward too. I found if I got stuck because of not knowing the function or type to use from Fable.Import.Browser, I could just look up the corresponding javascript dom function/element on the Mozilla Dom API reference and the corresponding Fable module/type/function was named similarly.
For the memory tiles game, I needed a way to add a click event to a, “Start Fresh Game” button to refresh the grid with new randomized tiles. This is shown below:

let startNewGameButton = Browser.document.createElement("button")
      startNewGameButton.addEventListener_click(fun _ -> startNewGameCallback() :> obj)
      startNewGameButton.innerText <- "Start Fresh Game"
      gameContainer.appendChild startNewGameButton |> ignore

Here, I create the button to start a new game of randomized tiles. I then add a click event which can take a regular F# function as a parameter. Here I’m passing a lambda which, when executed, will call a function called startNewGameCallback. The block of code shown here exists inside a render function in my View module and this render function takes the startNewGameCallback function as a parameter. The full source code for this is on my github MemoryTiles.fsx

I use the same Fable addEventListener_click function for handling the click event on each tile as shown below.

let eventHandler r c d = Func<Browser.MouseEvent, obj>(fun _ -> tileClickCallback r c gameBoard :> obj)
        tileDiv.addEventListener_click(eventHandler rowIndex cIndex tileDiv)

This takes a lambda wrapping another callback which has been passed to the render function in my View module. This eventually calls back to the main driver of the game – the tileClick function in my Controller module, but first let me just show the data modelling from the Model module to put this tileClick function in context.

F# typing goodness with Fable

Fable supports the standard F# data modelling constructs bringing us the F# type safety that we love so much!
For the games’ backing model, I used standard F# records and disciminated unions to represent the immutable game board which serves as a point in time state to render a point in time view and also to enable the generation of a another game board depending on user input and the value of its tiles. A tile itself is modelled as a record and the status of a tile along with the the current selection of tiles are modelled as DUs. So even though this is a game running as javascript, I have still been able to use F# types and F# immutability to express the domain and behaviour of the game. This is shown below. I added a GameBoard helper module for the state transistions of updating tiles and the selection. There is some localized mutability within the updateTile function but the function is still pure with an immutable game board going in and another one coming out. The updateSelected is also returning a new immutable game board.

type GameBoard = {
    Selection : Selection
    Board : Tile list list
}
and Tile = {
    Row : int
    Col : int
    HiddenColor : string
    CoverColor : string
    Status : TileStatus
}
and TileStatus = UnMatched | AttemptingMatch | Matched
and Selection = NoneSelected | OneSelected of Tile | TwoSelected of Tile * Tile
 
module GameBoard = 
    let updateTile r c t gb = 
        let updatedRow = gb.Board.[r] |> List.toArray |> (fun arr -> arr.[c] <- t; arr) |> List.ofArray
        {
            gb with
                Board = gb.Board |> List.toArray |> (fun arr -> arr.[r] <- updatedRow; arr) |> List.ofArray
        }
 
    let updateSelected s gb = { gb with Selection = s}

Using standard .Net events for to trigger view rendering

.Net event handling is one of my favourite features of the .Net platform and, in particular, the way it is implemented in F#. Fable allows us to use standard .Net event triggering and handling. I designed the game using a version of the model, view, controller pattern. I wanted to keep my view decoupled from the model but I still needed a way for it to be rendered whenever the game board changed. I also wanted it to remain as decoupled as possible from the controller module. The controller module passes the main engine of the game, its tileClick function, as a function parameter to the view but the view doesn’t know anything about its implementation. This is similar for the callback to start a fresh game. In order for the view to be rendered and re-rendered, I set up a an event in the in the Model module as follows.

let modelChangeEvent = (new Event<GameBoard>())

In the Controller module, there is a an initialise function which starts everything off. It publishs an observation to the above event and adds a listener, which takes a game board as a parameter and which will call render on the View whenever the a modelChangeEvent is triggered.
A startGame() function, called by initialise, simply triggers a modelChangeEvent to trigger the initial rendering of the game.

let startGame() = modelChangeEvent.Trigger (Model.generateRandomModel 4)
 
let initialise() = 
    Model.modelChangeEvent.Publish.Add(fun gameBoard -> View.render tileClick startGame gameBoard)
    startGame()

Main game logic with F# pattern matching

The main game logic is in the tileClick function of the Controller module. It receives a game board and a row and column for the clicked tile and, based on these, it determines a new game board to be created and triggers the modelChangeEvent so that the View.render function is called to re-render with the new game state.For some cases, no event is triggered and the view is not re-rendered e.g. if the same tile is clicked multiple times in succession. Standard F# pattern matching can be used here and the full function is below.

let tileClick tileRow tileCol (gameBoard: GameBoard) = 
    let board = gameBoard.Board
    let tile = board.[tileRow].[tileCol] 
    let lastSelection = gameBoard.Selection
    if tile.Status = Matched then ()
    else
        match lastSelection with
        | TwoSelected(t1, t2) when t1.Status = Matched && t2.Status = Matched && tile <> t1 && tile <> t2->
            gameBoard
            |> GameBoard.updateTile tile.Row tile.Col { tile with Status = AttemptingMatch }
            |> GameBoard.updateSelected (OneSelected { tile with Status = AttemptingMatch })
            |> modelChangeEvent.Trigger
        | TwoSelected(t1, t2) when t1.Status = Matched && tile <> t1 && tile <> t2 -> 
            gameBoard
            |> GameBoard.updateTile tile.Row tile.Col { tile with Status = AttemptingMatch }
            |> GameBoard.updateTile t2.Row t2.Col { t2 with Status = UnMatched }
            |> GameBoard.updateSelected (OneSelected { tile with Status = AttemptingMatch })
            |> modelChangeEvent.Trigger
        | TwoSelected(t1, t2) when t2.Status = Matched && tile <> t1 && tile <> t2 -> 
            gameBoard
            |> GameBoard.updateTile tile.Row tile.Col { tile with Status = AttemptingMatch }
            |> GameBoard.updateTile t1.Row t1.Col { t1 with Status = UnMatched }
            |> GameBoard.updateSelected (OneSelected ({ tile with Status = AttemptingMatch }))
            |> modelChangeEvent.Trigger
        | TwoSelected(t1, t2) when tile <> t1 && tile <> t2 -> 
            gameBoard 
            |> GameBoard.updateTile t1.Row t1.Col { t1 with Status = UnMatched }
            |> GameBoard.updateTile t2.Row t2.Col { t2 with Status = UnMatched }
            |> GameBoard.updateTile tile.Row tile.Col { tile with Status = AttemptingMatch }
            |> GameBoard.updateSelected (OneSelected ({ tile with Status = AttemptingMatch }))
            |> modelChangeEvent.Trigger           
        | OneSelected t1 when t1.HiddenColor = tile.HiddenColor && tile <> t1 -> 
            gameBoard
            |> GameBoard.updateTile t1.Row t1.Col { t1 with Status = Matched }
            |> GameBoard.updateTile tile.Row tile.Col { tile with Status = Matched }              
            |> GameBoard.updateSelected (TwoSelected ({ t1 with Status = Matched },{ tile with Status = Matched }))
            |> modelChangeEvent.Trigger
        | OneSelected t1 when tile <> t1 -> 
            gameBoard
            |> GameBoard.updateTile tile.Row tile.Col { tile with Status = AttemptingMatch }              
            |> GameBoard.updateSelected (TwoSelected (t1, { tile with Status = AttemptingMatch }))
            |> modelChangeEvent.Trigger 
        | NoneSelected when tile.Status <> Matched -> 
            gameBoard
            |> GameBoard.updateTile tile.Row tile.Col { tile with Status = AttemptingMatch }              
            |> GameBoard.updateSelected (OneSelected ({ tile with Status = AttemptingMatch }))
            |> modelChangeEvent.Trigger
        | _ -> ()

Conclusion

I had loads of fun making this game and definitely want to add more to it. My daughter has been playing away with it and having great fun!
I’ll definitely be continuing on my Fable journey.
The full source code for this game is on my github:
MemoryTiles.fsx
Also the current version of the game can be played here

F# Unit Testing Simplified – Expecto with Visual Studio Code

This post is a slightly shorter follow on post to my last one, F# Has Won Me Over: Coming to .Net World from Outside .Net

In that post, I showed how to set up a basic F# development workflow using Visual Studio Code. It probably seemed a little overkill just to get simple tests up and running.I’m glad I took this longer approach though as, coming into .NET world, it made me aware of how the tools and F# projects fit together under the hood – something that wouldn’t be as clear if I went the all out IDE approach.

Since publishing that blog, I have gotten a lot of really helpful feedback from others in the F# community and I have taken a look at an F# testing tool called Expecto

The Ionide plugin for Visual Studio Code has a really nice integration for running tests using Expecto – it’s a whole lot simpler then the workflow I showed in my last post!

Following from my last post, lets add a different test project to test the MusicLibrary project and we’ll use Expecto this time to run our test.
Again, I’m running Visual Studio Code on Mac OSX in this example but the steps should be more or less the same for other operating systems.

So, back in Visual Studio Code, type cmd-shift-p to open the command palette
Type F# and select F#:New Project

Choose a console as this will give us a main entry point for Expecto to run tests.

You will be prompted for the name of the project directory.

Lets just call it MusicLibraryExpectoTests

You will then be prompted for the name of the project.

Again, MusicLibraryExpectoTests will be grand.

Now we need to add our Expecto dependencies with Paket.

So open up the newly creted MusicLibraryExpectoTests directory and open MusicLibraryExpectoTests.fsproj

Open the command palette again, cmd-shift-p
Type Paket and select Paket:Add NuGet Package (to current project)

Then type Expecto and Enter.

You will see, info in the Paket output terminal.

Afterwards, you should be able to search Expecto in the MusicLibraryExpectoTests.fsproj file and find something like

   
 ..\..\packages\Expecto\lib\net40\Expecto.dll
               

Repeat for
Expecto.BenchmarkDotNet
Expecto.FsCheck

After all this, our paket.references file within the MusicLibraryExpectoTests project should contain

FSharp.Core
Expecto
Expecto.BenchmarkDotNet
Expecto.FsCheck

Lets try out a hello world expecto test. So, open up MusicLibraryExpectoTests.fs which will have automatically been created for us when we created our MusicLibraryExpectoTests project.

Replace the contents of that file with the following:

module MusicLibraryExpectoTests

open Expecto

[<Tests>]
let tests =
  testCase "yes" <| fun () ->
    let subject = "Hello World"
    Expect.equal subject "Hello world"
                 "The strings should equal"


[<EntryPoint>]
let main args =
  runTestsInAssembly defaultConfig args

and save.
Here we have added one test case and a main function as an entry point for expecto to run our tests.
We can easily run our test simply as follows:
Open command palette, cmd-shift-p
Type Ex and choose Expecto:Run

In the expecto output terminal, we can see our failing test!

We can fix our failing test by changing that ‘w’ to upper case so our MusicLibraryExpectoTests.fs becomes:

module MusicLibraryExpectoTests

open Expecto

[<Tests>]
let tests =
  testCase "yes" <| fun () ->
    let subject = "Hello World"
    Expect.equal subject "Hello World"
                 "The strings should equal"

[<EntryPoint>]
let main args =
  runTestsInAssembly defaultConfig args

This time to run the tests, lets just use a shortcut, ctrl-f6
Now, our test passes and you should see this along with other output in the Expecto terminal output in Visual Studio Code:

[21:28:12 INF] EXPECTO?! Summary...
Passed:  1
	yes 
Ignored: 0
	Failed:  0
	Errored: 0

Now, we can use Expecto to test our addSongToLibrary function from the MusicLibrary project that I went through in my last post.
We need to link our MusicLibraryExpectoTests project to this library first. So, open the MusicLibraryExpectoTests.fsproj file again.

Then we can use the command palette, cmd-shift-p to add a project reference.

Choose MusicLibraryExpectoTests for the project to edit

and MusicLibrary as the project to reference

Your MusicLibraryExpectoTests.fsproj should now contain something like

    
ProjectReference Include="../../MusicLibrary/MusicLibrary/MusicLibrary.fsproj"

We can add our test for addSongToLibrary to MusicLibraryExpectoTests.fs as follows:

module MusicLibraryExpectoTests

open Expecto
open MusicLibrary

[<Tests>]
let tests =
  testList "test group" [
    testCase "yes" <| fun _ ->
        let subject = "Hello World"
        Expect.equal subject "Hello World"
                    "The strings should equal"

    testCase "addSongToLibrary adds song to library" <| fun _ ->
        let expected = ["Sir Duke";"Superstition"]   
        Expect.equal expected (addSongToLibrary ["Superstition"] "Sir Duke")
                    "The library list should contain the new song"
    ]

[<EntryPoint>]
let main args =
  runTestsInAssembly defaultConfig args

Now, we can run the tests with ctrl-F6

And in the Expecto terminal output in Visual Studio code,
you should see something like this along with other output

Passed:  2
	test group/yes ....
       ....
	test group/addSongToLibrary adds song to library [/Users/tom/Dropbox/software-
Ignored: 0
	Failed:  0
	Errored: 0

Conclusion

There is a lot more you can do with Expecto. I have just scratched the surface here but, even with the basics here, you can very quickly create and run test cases from within Visual Studio Code. It’s definitely a tool that I will be using a lot!

F# Has Won Me Over: Coming to .Net World from Outside .Net

F# is a language that was sitting in the corner calling to me for a long time tempting me to go take a look at it. I watched Scott Wlaschin’s fantastic talk on Functional Programming Design Patterns some time ago. This talk is a walk through of common FP design patterns with the example code being in F#. Scott put together this talk so well that it is not a blocker to getting the most from the talk if you don’t have any previous knowledge of F#. At the same time, coming from mostly programming in Scala professionally, I couldn’t help but admire the beauty of the F# code in his examples. So I had a quick look further at some F# code out there. Things like a simple syntax using whitespace, automatic currying, nice concise representations of Algebraic Data Types caught my eye immediately.

Over the Christmas holidays I dived into F# by taking courses on FSharp TV and by going through content from Scott’s wonderful blog Fsharp For Fun And Profit.

F# has quickly become my favourite language to code in along with Haskell. Along the way though, there were some stumbling blocks getting used to the .Net world. Most of these could have been avoided if I just used the Xamarin Studio IDE. However, I’m glad I didn’t and took a little pain along the way as it made be explore the .Net and F# ecosystem. I did start to use Visual Studio Code which is really a text editor with a load of great plugins out there for different languages. Its lightweight enough that you still have to find out what’s happening under the hood a bit with your F#  .Net projects but, at the same, it does make things like creating projects with common templates, building and adding references to existing projects a lot less painful. Incidentally, there is now a Haskell plugin for Visual Studio Code which uses Intero. This is very exciting indeed and I can’t wait to try it out!

How do I code in F# on a mac?!

Yep this is a question I had for a while too. That is until I discovered Mono, an open source .Net framework which is cross platform.
I found the best way to get F# set up on OSX was to install Mono but to actually use the installer that can be downloaded here. I tried installing the homebrew way first but this seems to be missing some .Net libraries. For example, one that is needed for System.Drawing. I uninstalled my homebrew mono installation and installed from the Mono download that I linked to above, and I was then able to build and draw lovely spirographs from the FSharp TV, Introduction to F# course.

How do I create an F# project?

This was a question I had too. I wanted to simply create a project with a common folder structure. I was looking for something similar to lein new in Clojure or stack new in Haskell. A great tool for this is Forge. The Visual Studio Code ionide plugin integrates with Forge to make it really easy to create new projects through the command palette in Visual Studio Code. I demo this further down.

How do I manage dependencies?

The tool for this is Paket which uses the Nuget package manager under the hood. Again, Visual Studio Code has a plugin to integrate with Paket, Ionide-Paket

How do I build an F# project

FAKE is a very popular build tool for F# and allows you to specify your build tasks through a build.fsx file using and F# DSL. Visual Studio Code has a plugin to integrate with this too as part of its ionide suite, Ionide FAKE This is demoed further down in this blog too.

How do I run a .exe on Mac!

Some of these tools I have mentioned along with the nunit testing tool (which I demo further down) require the running of a .exe to perform tasks. If the .exe is one that doesn’t run directly on Mac OS or Linux, usually it can be run using

mono name-of-your-executable.exe

once mono is installed and on your path and once name-or-your-executable.exe is actually a file that can be executed. It may seem like a simple thing but it is important to know as there are resources out there that describe how to use .Net tools assuming you are on a Windows machine.

Demo Time: TDD with F#

As there are resources out there about setting up F# with Mono and Visual Studio Code, there’s no need to re-iterate them here so I will assume that you managed to get those set up. If you have questions, feel free to comment on this blog.
My main stumbling block coming from outside the .Net world was simply setting up a unit test project and linking this to a production code project (by production code, I mean implementation code).
Lets go through that here:

So in Visual Studio Code, from an empty directory, open command palette with shift-command-p and type F#.

Then select New Project.

You will be prompted to select the type of project.

Choose classlib for our production code.
You will then be prompted to choose the project directory.

Lets specify a directory called MusicLibrary. So type in MusicLibrary, hit enter and you will be prompted to provide a project name. Lets call this MusicLibrary too.

After a second or so, you should eventually see that the ionide plugin has created three directories, .packet, MusicLibrary and packages. It will have also created a build.fsx file and a build.sh file so that we can build our new MusicLibrary project with FAKE.

In the MusicLibrary directory, you will find a newly created MusicLibrary.fs file.

This is where we are going to put our production code after we write a test for it.
For now, delete everything from there and we are going to add a function to test called addSongToLibrary which will eventually take a list of existing song titles and a new song title and return a list of song titles containing our the existing ones and our new title. For now, it just returns an empty list until we have our test ready. The contents of the MusicLibrary.fs file can be replaced with:

module MusicLibrary

let addSongToLibrary existingSongs newSong = []

Now, we should be able to build this right?. Lets give it ago.

Open the Visual Studio command palette again and type FAKE.

Select Build

You will be prompted for a FAKE build task to run.

Select build.

Now we get an error:

If you look down at the output window in Visual Studio Code, you will see something like
build.sh: line 34: syntax error: unexpected end of file

We need to convert the dos .sh file to into unix format. There is a tool for this on the Mac App Store dos2unix.

Install that.
In the terminal go into the parent directory of your MusicLibrary directory where build.sh exists and run dos2unix on the build.sh file.

dos2unix build.sh
dos2unix: converting file build.sh to Unix format...

Now follow the steps earlier to try and do a FAKE build in visual studio code.
In the output in visual studio code, you should see output about using paket to get any needed dependencies and also about the creation of dlls.
From my understanding Dlls in .Net are like jar files in the jvm world and they are the binaries created as a result of compiling F# code. The FAKE build will now have created a build directory and put the dlls in there.

You will see a MusicLibrary.dll in there.

Back in the MusicLibrary directory there is also a MusicLibrary.fsproj file which FAKE uses to find out things it needs for the build. When we write our test, we will see how we can add a reference to the MusicLibrary.dll into our .fsproj file for our unit test project. If you look in build.fsx, you will see a reference to these .fsproj files for FAKE.

let appReferences  =
    !! "/**/*.csproj"
    ++ "/**/*.fsproj"

Lets set up our test project so we can test our addSongToLibrary function!

To test our addSongToLibrary function we can set up a MusicLibraryTest project. Follow the same steps as we did for creating the MusicLibrary project in visual studio code except for the prompt to choose library type, choose fsunit

After, you should see a newly created MusicLibraryTest directory.

If you go into that directory, you will see that there has been a MusicLibraryTest.fs file created for us containing:

module MusicLibraryTest

open NUnit.Framework
open FsUnit

[<Test>]
let ``Example Test`` () =
    1 |> should equal 1

There are 2 libraries imported here for unit testing, NUnit.Framework and FsUnit.
This is where paket comes in for our test dependencies. If you go into the paket.references file within the MusicLibraryTest directory you will see

FSharp.Core
FsUnit

So, we need to link our MusicLibraryTest project to our MusicLibrary.fsproj so that we can test our addSongToLibrary function. This is taken care of by add a reference to the MusicLibrary.fsproj in the MusicLibraryTest.fsproj.

Open up the MusicLibraryTest.fsproj file.
Now, open visual studio command palette again.

Select Add Project Reference.
Then choose MusicLibraryTest as the project that you want to edit

Then choose MusicLibrary as the reference

Now, somewhere in the MusicLibraryTest.fsproj, there will be a newly created reference to the MusicLibray.fsproj. Search the MusicLibraryTest.fsproj for MusicLibrary.fsproj.

Now we can reference our MusicLibrary module and call the addSongToLibrary function.
Back in the MusicLibraryTest.fs file, we can import the MusicLibrary module which contains our addSongToLibrary function and lets write a test to test adding a song title.

Replace the contents of MusicLibraryTest.fs with:

module MusicLibraryTest

open NUnit.Framework
open FsUnit

open MusicLibrary

[<Test>]
let shouldAddASong () =
    ["Sir Duke";"Superstition"] 
    |> should equal (addSongToLibrary ["Superstition"] "Sir Duke")

How do I run my test!

First of all lets create our MusicLibraryTest.dll. This is done the same way that we created MusicLibrary.dll before by running the FAKE build in visual studio code. After running the build, there should be a MusicLibraryTest.dll in your build directory.


Actually getting tests to run was a stumbling block for me. To run tests, a FAKE task can be created in build.fsx. I will show how to do that in a little bit. First I will show how to run more manually using an nunit test runner.
The NUnit3 test runner can be downloaded as a zip file here.
To make this work with a FAKE task a bit later, first create a directory tools/Nunit in the root folder in visual studio code that you have your MusicLibrary and MusicLibraryTest projects in.
Then download NUnit.Console-3.5.0.zip into there.


Then unzip it.

Now we can use the nunit3-console.exe to run our test.
In the terminal, go into the tools/NUnit folder.
Make the nunit3-console.exe executable:

chmod 755 nunit3-console.exe

We can run our test with the command:

mono nunit3-console.exe ../../build/MusicLibraryTest.dll

This should output our failing test result:

NUnit Console Runner 3.5.0
Copyright (C) 2016 Charlie Poole

Runtime Environment
   OS Version: MacOSX 16.1.0.0
  CLR Version: 4.0.30319.42000

Test Files
    ../../build/MusicLibraryTest.dll


Errors and Failures

1) Failed : MusicLibraryTest.shouldAddASong
  Expected is <Microsoft.FSharp.Collections.FSharpList`1[System.Object]>, actual is <Microsoft.FSharp.Collections.FSharpList`1[System.String]>
  Values differ at index [0]
at FsUnit.TopLevelOperators.should[a,a] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] f, a x, System.Object y) [0x0004e] in :0
at MusicLibraryTest.shouldAddASong () [0x0003d] in :0

Run Settings
    DisposeRunners: True
    WorkDirectory: /Users/tom/Dropbox/software-development/fsharp/NUnitDemo/tools/Nunit
    ImageRuntimeVersion: 4.0.30319
    ImageRequiresX86: False
    ImageRequiresDefaultAppDomainAssemblyResolver: False
    NumberOfTestWorkers: 8

Test Run Summary
  Overall result: Failed
  Test Count: 1, Passed: 0, Failed: 1, Inconclusive: 0, Skipped: 0
    Failed Tests - Failures: 1, Errors: 0, Invalid: 0
  Start time: 2017-01-08 13:40:33Z
    End time: 2017-01-08 13:40:33Z
    Duration: 0.142 seconds

Now lets integrate this into FAKE.

In the build.fsx file, add the following near the top underneath
open Fake

add

open Fake.Testing

so you now have

open Fake
open Fake.Testing

Add our NUnitTest task underneath the Target “Deploy” block.

let testDlls = !! (buildDir + "*Test.dll")

Target "NUnitTest" (fun _ ->
  testDlls
    |> NUnit3 (fun p -> p)

)

So we have:

Target "Deploy" (fun _ ->
    !! (buildDir + "/**/*.*")
    -- "*.zip"
    |> Zip buildDir (deployDir + "ApplicationName." + version + ".zip")
)

let testDlls = !! (buildDir + "*Test.dll")

Target "NUnitTest" (fun _ ->
  testDlls
    |> NUnit3 (fun p -> p)

)

Also add NUnitTest to the build order so the end of your build.fsx file should be

// Build order
"Clean"
  ==> "Build"
  ==> "NUnitTest"
  ==> "Deploy"

// start build
RunTargetOrDefault "Build"

Open Visual Studio Code command palette and type FAKE as before

Choose FAKE: Build

Now you should see our new NUnitTest task so select that.

This will fail because of our failing test.

The output pane in visual studio code will have the results of the nunit3-console.exe runner

1) Failed : MusicLibraryTest.shouldAddASong
  Expected is <Microsoft.FSharp.Collections.FSharpList`1[System.Object]>, actual is <Microsoft.FSharp.Collections.FSharpList`1[System.String]>
  Values differ at index [0]
at FsUnit.TopLevelOperators.should[a,a] (Microsoft.FSharp.Core.FSharpFunc`2[T,TResult] f, a x, System.Object y) [0x0004e] in :0
at MusicLibraryTest.shouldAddASong () [0x0003d] in :0

Lets make the test pass!!

Go back to the MusicLibrary.fs file and make the test pass by replacing its contents with:

module MusicLibrary

let addSongToLibrary existingSongs newSong = newSong :: existingSongs

In Visual Studio Code, use the command palette to run FAKE: Build selecting build as before and then  FAKE: Build selecting NUnitTest.

Looking through the output in visual studio code we can now see our passing test report:

Test Run Summary
  Overall result: Passed
  Test Count: 1, Passed: 1, Failed: 0, Inconclusive: 0, Skipped: 0
  Start time: 2017-01-08 13:46:29Z
    End time: 2017-01-08 13:46:29Z
    Duration: 0.132 seconds

Conclusion

Hopefully this has helped people to get started with the F# and .Net ecosystem and get up and running quickly in this amazing language.