Mountebank Mocks with F#

Apr 22, 2014

This post describes how to use Mountebank imposters as Mock Objects/Test Spies via HTTP with F#.

As a learning exercise, for mountebank, but also for F#, I wrote a few F# functions to communicate with Mountebank imposters via HTTP and a few passing tests. The rest of the post describes what I learned so far.

What is Mountebank?

Mountebank is a tool (and currently the only one) which provides multi-protocol, multi-language, on-demand, Test Doubles over the wire, named imposters.

Imposters are normally created during the fixture setup phase and disposed in the fixture teardown phase.

According to the official website, Mountebank currently supports imposters for:

  • http
  • https
  • tcp
  • smtp

Smtp example

Imposters can act as Mocks, as well as Stubs. For Mocks, the Mountebank website contains a mocking example for the STMP protocol.

Scenario: SMTP client transmits a mail message; verify the subject of the message.

The Mountebank server runs at 192.168.1.3 on port 2525 in a Unix-like OS. During the setup phase an imposter is created via HTTP POST specifying the SMTP protocol and port (4547) in the request body:

POST http://192.168.1.3:2525/imposters/ HTTP/1.1
Host: 192.168.1.3:2525
Accept: application/json
Content-Type: application/json

{ 
  "port": 4547, 
  "protocol": "smtp" 
}

In F# this can be written using the FSharp.Data Http module as:

let create protocol host port =
    Http.Request(
        "http://" + host + ":2525/imposters/",
        headers = 
            ["Content-Type", HttpContentTypes.Json; 
             "Accept"      , HttpContentTypes.Json],
        httpMethod = "POST",
        body = TextRequest 
                   (sprintf @"{ ""port"": %i, ""protocol"": ""%s"" }" 
                       port protocol))

The response is:

HTTP/1.1 201 Created
Location: http://192.168.1.3:2525/imposters/4547
Content-Type: application/json; charset=utf-8
Content-Length: 167
Date: Mon, 21 Apr 2014 20:42:57 GMT
Connection: keep-alive

{
  "protocol": "smtp",
  "port": 4547,
  "requests": [],
  "stubs": [],
  "_links": {
    "self": {
      "href": "http://192.168.1.3:2525/imposters/4547"
    }
  }
}

Now we can send a mail message via SMTP to the Mountebank imposter using port 4547:

From: "Customer Service" <code@nikosbaxevanis.com>
To: "Customer" <nikos.baxevanis@gmail.com>
Subject: Thank you for your order

Hello Customer,
Thank you for your order from company.com.  Your order will
be shipped shortly.

Your friendly customer service department.

In F# this can be written as:

let expectedSubject = "Thank you for your order"
(new SmtpClient("192.168.1.3", 4547)).Send(
    new MailMessage(
        "code@nikosbaxevanis.com", 
        "nikos.baxevanis@gmail.com", 
        expectedSubject, 
        "Hello Customer, Thank you for your order from company.com."))

To get the captured requests from the imposter we can issue a GET or a DELETE request. Normally, this happens during the fixture teardown phase via DELETE:

DELETE http://192.168.1.3:2525/imposters/4547 HTTP/1.1
Content-Type: application/json
Host: 192.168.1.3:2525

According to Hypertext Transfer Protocol -- HTTP/1.1 for HTTP method definitions, it is OK to issue a DELETE to also get the captured requests from the imposter: *"A successful response SHOULD be 200 (OK) if the response includes an entity describing the status" -- Hypertext Transfer Protocol -- HTTP/1.1, 9.7 DELETE*.

The response is:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 810
Date: Mon, 21 Apr 2014 20:41:10 GMT
Connection: keep-alive

{
"protocol": "smtp",
"port": 4547,
"requests": [
  {
    "requestFrom": "192.168.1.4",
    "envelopeFrom": "code@nikosbaxevanis.com",
    "envelopeTo": [
      "nikos.baxevanis@gmail.com"
    ],
    "from": {
      "address": "code@nikosbaxevanis.com",
      "name": ""
    },
    "to": [
      {
        "address": "nikos.baxevanis@gmail.com",
        "name": ""
      }
    ],
    "cc": [],
    "bcc": [],
    "subject": "Thank you for your order!",
    "priority": "normal",
    "references": [],
    "inReplyTo": [],
    "text": "Hello Customer, Thank you for your order from company.com.\n",
    "html": "",
    "attachments": []
  }
],
"stubs": [],
"_links": {
  "self": {
    "href": "http://192.168.1.3:2525/imposters/4547"
  }
}

In F# the requests JSON property can be decomposed and extracted using the FSharp.Data JSON Parser and Http modules as:

let getCapturedRequests (spy : HttpResponse) = 
    let getRequests jsonValue = 
        match jsonValue with
        | FSharp.Data.Record properties ->
            match properties with
            | [| protocol; port; requests; stubs; _links; |] ->
                match snd requests with
                | FSharp.Data.Array elements ->
                    match elements |> Seq.toList with
                    | head :: tail -> Some(elements)
                    | [] -> None
                | _ -> None
            | _ -> None
        | _ -> None
    let response = 
        Http.Request(
            spy.Headers.Item "Location",
            headers = [ 
                "Content-Type", HttpContentTypes.Json; 
                "Accept", HttpContentTypes.Json ],
            httpMethod = "DELETE")
    match response.Body with
    | Text json -> 
        JsonValue.Parse json 
        |> getRequests
    | _ -> None

The signature of GetCapturedRequests function is:

spy : HttpResponse -> JsonValue [] option

The value of the subject property can be similarly decomposed and extracted with Pattern Matching:

match GetCapturedRequest imposter "subject" with
| Some actual -> expectedSubject = actual
| None -> 
    raise <| InvalidOperationException(
        "No property named 'subject' found in captured requests.") 

With all the above a test using xUnit.net and composed assertions with Unquote could be written as:

let verify = Swensen.Unquote.Assertions.test
    
[<Fact>]
let sendMailTransmitsCorrectSubject () =
    let expectedSubject = "Thank you for your order!"
    let mountebankHost = "192.168.1.3"
    let imposterPort = 4547
    let spy = Imposter.Create "smtp" mountebankHost imposterPort

    (new SmtpClient(mountebankHost, imposterPort)).Send(
        new MailMessage(
            "code@nikosbaxevanis.com", 
            "nikos.baxevanis@gmail.com", 
            expectedSubject, 
            "Hello Customer, Thank you for your order from company.com."))

    verify <@
            match SmtpSpy.GetCapturedRequest spy "subject" with
            | Some actual -> expectedSubject = actual
            | None        -> 
                raise <| InvalidOperationException(
                    "No property named 'subject' found in captured requests.") @>

The Mountebank website uses the Mock Object terminology when verifying indirect outputs. However, the examples shown here don't setup expectations - instead they only verify *captured*-indirect outputs of the SUT; thus the Test Spy terminology is used in code.

Scenario: SMTP client transmits correct number of requests.

In this case, it’s only necessary to verify that the SMTP request on the imposter was made only once:

[<Fact>]
let sendMailTransmitsCorrectNumberOfSmtpRequests () =
    let expectedNumberOfRequests = 1
    let mountebankHost = "192.168.1.3"
    let imposterPort = 4546
    let spy = Imposter.Create "smtp" mountebankHost imposterPort
    
    (new SmtpClient(mountebankHost, imposterPort)).Send(
        new MailMessage(
            "code@nikosbaxevanis.com", 
            "nikos.baxevanis@gmail.com", 
            "Thank you for your order!", 
            "Hello Customer, Thank you for your order from company.com."))

    verify <@
            match Imposter.GetCapturedRequests spy with
            | Some actual -> expectedNumberOfRequests = (actual |> Seq.length)
            | None -> 
                raise <| InvalidOperationException(
                    sprintf "Expected %i calls but received none." 
                        expectedNumberOfRequests) @>

The complete source code is available on this gist - any comments or suggestions are always welcome.

You may also read the next post, Mountebank stubs with F#.

Idioms.FsCheck

Mar 25, 2014

AutoFixture 3.18 introduces a new glue library called Idioms.FsCheck.

It uses FsCheck to implement a reusable assertion that verifies (or at least, makes probable) that a method doesn’t return null:

[Theory, UnitTestConventions]
public void Scenario(ISpecimenBuilder builder)
{
    var sut = from x in new Methods<SomeClass>() 
              select x.SomeMethod();
    var assertion = new ReturnValueMustNotBeNullAssertion(builder);
    Assert.Throws<ReturnValueMustNotBeNullException>(() => 
        assertion.Verify(sut));
}

How it works

If a method is parameterless the assertion simply exercises its return value by invoking it. Otherwise, its return value is exercised against arbitrary values generated by FsCheck.

An injected instance of an ISpecimenBuilder (e.g. a Fixture instance) creates instances of the type containing the method if it’s declared as an instance method.

When needed, AutoFixture and FsCheck can be customized via their DSLs.

Samples

  • Idioms.FsCheck’s test suite contains examples in F#.
  • The original question on Stack Overflow that triggered this feature includes examples in C#.

Installation

Idioms.FsCheck is available on NuGet:

PM> Install-Package AutoFixture.Idioms.FsCheck

How to configure AutoFoq with Foq's return strategy

Feb 23, 2014

This post explains how to configure AutoFixture.AutoFoq 3 so that null values are never returned when using Foq 1.5.1 and newer.

Foq’s behavior for mock objects that have not been explicitly setup, is to return null if the return type of a function is a reference type (e.g. a string):

type IInterface =
   abstract DoSomething : unit -> string

let sut = Mock<IInterface>().Create()
// No expectations have been setup.

let actual = sut.DoSomething()
// -> actual = null

AutoFixture is an opinionated library, and one of the opinions it holds is that nulls are invalid return values.

By the time AutoFixture.AutoFoq 3 was published Foq was still in version 1.0 with no specific hooks to override this behavior.

Foq now provides the necessary hooks to override this behavior and the rest of this post explains how to automate this when using AutoFixture.AutoFoq.

Solution

Ruben Bartelink originally discussed and proposed in Foq discussions about a returnStrategy argument for members that have not been explicitly setup:

let sut = Mock<IInterface>(returnStrategy = fun x -> "123" :> obj).Create()
// No expectations have been setup - fallback to returnStrategy function.

let actual = sut.DoSomething()
// -> actual = "123"

Configuring AutoFoq to use Foq’s returnStrategy argument

The existing AutoFoqCustomization has no specific hook to select Foq’s new returnStrategy argument. This can be addressed when necessary with the customization shown below:

[<AutoOpen>]
module internal SynthesizerMethod =
    type private SynthesizerMethod<'T when 'T : not struct>
        (parameterInfos, builder) =
        interface IMethod with
            member this.Parameters = parameterInfos
            member this.Invoke parameters = 
                Mock<'T>(SpecimenContext(builder).Resolve).Create(
                    parameters |> Seq.toArray) :> obj

    let Create (targetType: Type, parameterInfos: ParameterInfo[], builder) = 
        Activator.CreateInstance(
            typedefof<SynthesizerMethod<_>>
                .MakeGenericType(targetType), 
            parameterInfos,
            builder)

[<AutoOpen>]
module internal SynthesizerType = 
    type Type with 
        member this.GetPublicAndProtectedConstructors () = 
            this.GetConstructors(
                BindingFlags.Public ||| 
                BindingFlags.Instance ||| 
                BindingFlags.NonPublic)

type internal SynthesizerMethodQuery (builder) =
    do if builder = null then raise <| ArgumentNullException("builder")
    interface IMethodQuery with
        member this.SelectMethods target = 
            if target = null then raise <| ArgumentNullException("target")
            if target.IsInterface then 
                seq { yield Create(target, Array.empty, builder) :?> IMethod }
            else
                target.GetPublicAndProtectedConstructors() 
                |> Seq.sortBy(fun x -> x.GetParameters().Length)
                |> Seq.map(fun ctor -> 
                    Create(target, ctor.GetParameters(), builder) :?> IMethod)

    member this.SelectMethods targetType = 
        (this :> IMethodQuery).SelectMethods targetType

type internal AutoFoqSynthesizeReturnValuesCustomization () =
    interface ICustomization with 
        member this.Customize fixture = 
            match fixture with
            | null -> raise (ArgumentNullException("fixture"))
            | _    -> fixture.Customizations.Add(
                        FilteringSpecimenBuilder(
                            MethodInvoker(
                                SynthesizerMethodQuery(fixture)),
                            AbstractTypeSpecification()))

    member this.Customize fixture = (this :> ICustomization).Customize fixture

The only difference from the original AutoFoqCustomization is the usage of Foq’s returnStrategy argument in the SynthesizerMethodQuery class.

Typical usage

[<Fact>]
let CustomizationFillsReturnValues () = 
    let fixture = Fixture().Customize(AutoFoqSynthesizeReturnValuesCustomization())
    
    let sut = fixture.Create<IInterface>()
    // No expectations have been setup - fallback to Foq's returnStrategy function.

    let actual = sut.DoSomething()
    // -> actual: "f5cdf6b1-a473-410f-95f3-f427f7abb0c7"

For any members that have not been explicitly setup Foq returns constrained non-deterministic values generated by AutoFixture's equivalence classes, for example random numbers.

Declarative usage

The above test can be also written declaratively using AutoData theories:

type TestConventionsAttribute() = 
    inherit AutoDataAttribute(
        Fixture().Customize(AutoFoqSynthesizeReturnValuesCustomization()))

[<Theory; TestConventions>]
let CustomizationFillsReturnValuesDecleratively (sut : IInterface) =
    let actual = sut.DoSomething()
    // -> actual: "f5cdf6b1-a473-410f-95f3-f427f7abb0c7"

Levels of open-source code

Feb 13, 2014

A maturity model for open-source code:

Level 0

Only snapshots are available.

Level 1

Every single commit is available.

Level 2

Every single commit is available, contributions are accepted.

Level 3

Every single commit is available, contributions are accepted and discussions are taking place in the open.

Auto-Mocking with Foq and AutoFixture

Oct 19, 2013

A new Auto-mocking extension have been recently added to AutoFixture allowing the mock instances to be created by Foq.

This brings the total number of Auto-mocking extensions for AutoFixture up to five:

Install

To install AutoFixture with Auto Mocking using Foq, run the following command in the Package Manager Console:

PM> Install-Package AutoFixture.AutoFoq

To use it, add an AutoFoqCustomization to the Fixture instance:

let fixture = Fixture().Customize(AutoFoqCustomization())

Usage

In the test below the mocked instance is created automatically by Foq:

[<Fact>]
let AutoMockInterfaceAndSetupExpectations() =
    // Fixture setup
    let fixture = Fixture().Customize(AutoFoqCustomization())
    let dummy = obj()
    // Exercise system
    let sut = fixture.Create<IInterface>()
    sut.MakeIt(dummy) |> ignore
    // Verify outcome
    Mock.Verify(<@ sut.MakeIt(dummy) @>, Times.Once)
    // Teardown

The above test can be written declaratively using AutoData theories:

[<Theory>][<AutoFoqData>]
let AutoMockInterfaceAndSetupExpectationsDeclaratively 
  (sut: IInterface, dummy: obj) =
    sut.MakeIt(dummy) |> ignore
    Mock.Verify(<@ sut.MakeIt(dummy) @>, Times.Once)

The [AutoFoqData] attribute is defined as:

type AutoFoqDataAttribute() = 
    inherit AutoDataAttribute(
        Fixture().Customize(AutoFoqCustomization()))

An automatically published release created from the latest successful build can be also downloaded from here.

subscribe via RSS