In the previous post I used Cabal to bootstrap QuickCheck with Hspec. In Haskell, Cabal is a build system which also doubles as a package manager.
This post attempts to do the same in F#; bootstrap FsCheck with xUnit.net, but instead of MSBuild and XML, I’m going to use Paket, Fake, and F#.
Install Paket, if not already installed:
// samples-fscheck.fsx
open System
open System.IO
Environment.CurrentDirectory <- __SOURCE_DIRECTORY__
if not (File.Exists "paket.exe") then
let url = "https://github.com/fsprojects/Paket/releases/download/0.26.3/paket.exe"
use wc = new Net.WebClient()
let tmp = Path.GetTempFileName()
wc.DownloadFile(url, tmp)
File.Move(tmp, Path.GetFileName url)
Reference Paket and specify the required packages:
// samples-fscheck.fsx
#r "paket.exe"
Paket.Dependencies.Install """
source https://nuget.org/api/v2
nuget FsCheck.Xunit
nuget FAKE
nuget xunit.runners
""";;
Reference Fake and specify the build parameters:
// samples-fscheck.fsx
#r "packages/FAKE/tools/FakeLib.dll"
open Fake
open Fake.FscHelper
let outputPath = Path.Combine(Environment.CurrentDirectory, "bin")
let outputFile = @"bin\GettingStarted.dll";
let references =
[ "packages/xunit/lib/net20/xunit.dll";
"packages/FsCheck/lib/net45/FsCheck.dll";
"packages/FsCheck.Xunit/lib/net45/FsCheck.Xunit.dll" ]
Use Fake’s DSL to define the build tasks:
// samples-fscheck.fsx
Target "CreateOutputPath" (fun _ ->
CreateDir outputPath)
Target "CleanOutputPath" (fun _ ->
CleanDir outputPath)
Target "CompileFiles" (fun _ ->
[ "tests/GettingStarted.fs" ]
|> Fsc (fun options ->
{ options with
Output = outputFile
FscTarget = Library
References = references }))
Target "CopyAssemblyReferences" (fun _ ->
CopyFiles outputPath references)
Target "RunTests" (fun _ ->
!! outputFile
|> xUnit (fun options -> options))
Compose the build tasks into a build pipeline:
// samples-fscheck.fsx
"CreateOutputPath"
==> "CleanOutputPath"
==> "CompileFiles"
==> "CopyAssemblyReferences"
==> "RunTests"
RunTargetOrDefault "RunTests"
Icebreaker:
Add a simple (icebreaker) test, because there’s always a bit of work involved in getting everything up and running:
// tests/GettingStarted.fs
module GettingStarted
open System
open FsCheck.Xunit
let add x y = x + y
[<Property>]
let ``icebreaker`` x y =
add x y = add y x
Run a build:
Run the following command in a terminal:
fsi samples-fscheck.fsx
See the tests running, and passing, via xUnit.net’s built-in test runner:
fsi samples-fscheck
found: <Path>\samples-fscheck\paket.dependencies
Resolving packages:
- exploring FAKE 3.14.9
- exploring xunit 1.9.2
- exploring xunit.runners 1.9.2
- exploring FsCheck 1.0.4
- exploring FsCheck.Xunit 1.0.4
Locked version resolutions written to <Path>\samples-fscheck
\paket.lock
Building project with version: LocalBuild
Shortened DependencyGraph for Target RunTests:
<== RunTests
<== CopyAssemblyReferences
<== CompileFiles
<== CleanOutputPath
<== CreateOutputPath
The resulting target order is:
- CreateOutputPath
- CleanOutputPath
- CompileFiles
- CopyAssemblyReferences
- RunTests
Starting Target: CreateOutputPath
<Path>\samples-fscheck\bin already exists.
Finished Target: CreateOutputPath
Starting Target: CleanOutputPath (==> CreateOutputPath)
Deleting contents of <Path>\samples-fscheck\bin
Finished Target: CleanOutputPath
Starting Target: CompileFiles (==> CleanOutputPath)
FSC with args:[|"-o"; "bin\GettingStarted.dll"; "-a"; "--platform:anycpu";
"--reference:packages/xunit/lib/net20/xunit.dll";
"--reference:packages/FsCheck/lib/net45/FsCheck.dll";
"--reference:packages/FsCheck.Xunit/lib/net45/FsCheck.Xunit.dll";
"tests/GettingStarted.fs"|]
Finished Target: CompileFiles
Starting Target: CopyAssemblyReferences (==> CompileFiles)
Finished Target: CopyAssemblyReferences
Starting Target: RunTests (==> CopyAssemblyReferences)
<Path>\samples-fscheck\packages\xunit.runners\tools\xunit.co
nsole.clr4.exe "<Path>\samples-fscheck\bin\GettingStarted.dl
l"
xUnit.net console test runner (64-bit .NET 4.0.30319.34209)
Copyright (C) 2013 Outercurve Foundation.
xunit.dll: Version 1.9.2.1705
Test assembly: <Path>\samples-fscheck\bin\GettingStarted.dll
1 total, 0 failed, 0 skipped, took 0.558 seconds
Finished Target: RunTests
---------------------------------------------------------------------
Build Time Report
---------------------------------------------------------------------
Target Duration
------ --------
CreateOutputPath 00:00:00.0018707
CleanOutputPath 00:00:00.0046358
CompileFiles 00:00:03.4156059
CopyAssemblyReferences 00:00:00.0058707
RunTests 00:00:01.9816487
Total: 00:00:05.4737549
Status: Ok
---------------------------------------------------------------------
See the test failing, as a form of Double Entry Bookkeeping:
// Change:
add x y = add y x
// To:
add x y = add 0 1
Re-run the command in a terminal:
fsi samples-fscheck
xUnit.net console test runner (64-bit .NET 4.0.30319.34209)
Copyright (C) 2013 Outercurve Foundation.
xunit.dll: Version 1.9.2.1705
Test assembly: <Path>\samples-fscheck\bin\GettingStarted.dll
GettingStarted.icebreaker [FAIL]
Falsifiable, after 1 test (2 shrinks) (StdGen (1751373120,295969456)):
(0, 0)
Stack Trace:
sorry no stacktrace
1 total, 1 failed, 0 skipped, took 0.679 seconds
Running build failed.
For convenience, all the above are also available on GitHub.