Multiple items
type CosTam =
  new : unit -> CosTam
  member ( here I want to test if page works correctly ) : unit -> 'a

Full name: index.CosTam

--------------------
new : unit -> CosTam
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
type Generators =
  static member muiKastomowyGeneratorek : obj

Full name: index.Generators
static member Generators.muiKastomowyGeneratorek : obj

Full name: index.Generators.muiKastomowyGeneratorek
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = System.Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
property Generators.muiKastomowyGeneratorek: obj
val config : obj

Full name: index.config

What is FsCheck?

FsCheck

What's the problem?

  • How to test functions which arguments admit a wide range of values
  • How to test functions which arguments needs to fulfill some restriction

How we solve the problem right now?

  • TestSuite in nUnit for various function inputs
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
using System;
using NUnit.Framework;

[TestFixture]
class TestSomething
{
    [TestCase(1, "ene")]
    [TestCase(2, "dułe")]
    [TestCase(3, "rabe")]
    [TestCase(4, "bocian")]
    [TestCase(5, "złapał")]
    [TestCase(6, "żabę")]
    public void TestCos(int paramFirst, string ParamSecond)
    {
        //do magic
        Assert.AreEqual(result, expectedResult);
    }
}

How we solve the problem right now?

  • Multiple specs
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
using developwithpassion.specifications.rhinomocks;
using Machine.Specifications;

public class TestSomething : Observes
{
    Establish that = () => //set something

    class When_something_return_this1
    {
        Because of = () => result = sut.DoSomething(1, "ene");
        It should_return_this = result.ShouldEquals(expectedResult);
    }

    class When_something_return_this2
    {
        Because of = () => result = sut.DoSomething(2, "dułe");
        It should_return_this = result.ShouldEquals(expectedResult);
    }
    //...
}

How we solve the problem right now?

  • Multiple specs (behaviours)
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
using developwithpassion.specifications.rhinomocks;
using Machine.Specifications;

public class TestSomething : Observes
{
    private Establish that = () => //set something
    [Behaviors]
    class ProperTest
    {
        result.ShouldEquals(expectedResult);
    }
    
    class When_something_return_this1
    {
        Because of = () => result = sut.DoSomething(1, "ene");
        Behaves_like<ProperTest> a_proper_test;
    }

    //...
}

FsCheck for a rescue!

How to start with FsCheck?

What we should install to start writting tests with FsCheck?

How syntax looks like?

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
open FsCheck
open NUnit.Framework

[<TestFixture>]
type CosTam() =
  [<Test>]
  member this.``here I want to test if page works correctly``() =
    use client = HttpClient()
    (
      let test(page: PageTest) =
        let url = 
          sprintf "%s/v1/Page?userId=%s&name=%s" endpointAddress userId page.name
        let response = client.GetAsync url
        let result = response.Result.StatusCode
        let label = 
          sprintf "Page %s returns %s instead of 200" page.name (result.ToString())
        (result = HttpStatusCode.OK)
          .Label(label)
      Check.QuickThrowOnFailure test
    )

How the pipeline of writing FsCheck test looks like?

  • tag class with TestFixture attribute
  • tag tests method with Test attribute
  • test method must be public, if it's not, then nunit will throw an exception method is not public
  • inside test define function with input parameters which should changed in every test execution
  • run test as Check.Quick(config) nameOfLocalFunction or Check.QuickThrowOnFailure nameOfLocalFunction
  • by default per every test, it is run with different input values 100 times

Shrinker

  • What he does?
  • If for some input a test fails, the input is being shrunk
  • It shinks input till it doesn't causes failure

Shrinker example (before shrinking)

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
<topic>
    <title>My first publication</title>
    <body>
        <image href="unicorn.pdf">
        <p>Hello <b>world!</b></p>
        <table>
            <title>table</title>
            <tbody>
                <row>
                    <entry>aaa</entry>
                </row>
            </tbody>
        </table>
    </body>
</topic>

Shrinker example (after shrinking)

1: 
2: 
3: 
4: 
5: 
6: 
<topic>
    <title/>
    <body>
        <p><b>w</b></p>
    </body>
</topic>

Generators

  • generators for simple datatypes are available by default
  • it is possible to create own generators for more complicated types
  • own generators could be register globally or per test

Custom generator

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
type Generators =
    static member muiKastomowyGeneratorek =
        Arb.generate<float>
        |> Gen.suchThat ((<) 0.)
        |> Arb.fromGen
        
Arb.register<Generators>() |> ignore

//using per tests
Check.Quick (forAll Generators.muiKastomowyGeneratorek test)

Custom configuration

  • by default test runs 100 times
  • granularity of generated data could be also changed
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
let config = {
    Config.Quick with
        MaxTest = 1000 //number of tests to run
        StartSize = 100 //default value is 1
        EndSize = 100 //default value is 100
}

Check.One(config, test)

Let's write a simple test

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
open FsCheck
open NUnit.Framework

[<TestFixture>]
type CosTam() =
  [<Test>]
  member this.``here I want to test if page works correctly``() =
    use client = HttpClient()
    (
      let test(page: PageTest) =
        let url = 
          sprintf "%s/v1/Page?userId=%s&name=%s" endpointAddress userId page.name
        let response = client.GetAsync url
        let result = response.Result.StatusCode
        let label = 
          sprintf "Page %s returns %s instead of 200" page.name (result.ToString())
        (result = HttpStatusCode.OK)
          .Label(label)
      Check.QuickThrowOnFailure test
    )

Run & Results

VisualStudio VisualStudioError TeamCity TeamCity2

Thanks :)