Unit Testing with Foq
@ptrelford on @c4fsharp March 2013
Go download on Nuget or foq.codeplex.com
Testing Language
what should a language for writing
                 acceptance tests be?




A Language for Testing
Tests operate by example
                 they describe specific
              scenarios and responses




A Language for Testing
I wonder if a different kind of
               programming language
                           is required.
                          - Martin Fowler 2003




A Language for Testing
UNIT TESTING WITH F#
F# as a Testing Language
F# NUnit                                    C# NUnit
module MathTest =                           using NUnit.Framework;

open NUnit.Framework                        [TestFixture]
                                            public class MathTest
                                            {
let [<Test>] ``2 + 2 should equal 4``() =
                                                [Test]
    Assert.AreEqual(2 + 2, 4)                   public void
                                                    TwoPlusTwoShouldEqualFour()
                                                {
                                                    Assert.AreEqual(2 + 2, 4);
                                                }
                                            }




NUnit
let [<Test>] ``2 + 2 should equal 4``() =
    2 + 2 |> should equal 4




FsUnit
let [<Test>] ``2 + 2 should equal 4``() =
    test <@ 2 + 2 = 4 @>




Unquote
.NET MOCKING
F# as a Testing Language
Mocking libraries
unittesting
var mock = new Mock<ILoveThisFramework>();

// WOW! No record/replay weirdness?! :)
mock.Setup(framework => framework.DownloadExists("2.0.0.0"))
   .Returns(true)
   .AtMostOnce();

// Hand mock.Object as a collaborator and exercise it,
// like calling methods on it...
ILoveThisFramework lovable = mock.Object;
bool download = lovable.DownloadExists("2.0.0.0");

// Verify that the given method was indeed called with the expected
value
mock.Verify(framework => framework.DownloadExists("2.0.0.0"));




Moq
// Creating a fake object is just dead easy!
// No mocks, no stubs, everything's a fake!
var lollipop = A.Fake<ICandy>();
var shop = A.Fake<ICandyShop>();

// To set up a call to return a value is also simple:
A.CallTo(() => shop.GetTopSellingCandy()).Returns(lollipop);

// Use your fake as you would an actual instance of the faked type.
var developer = new SweetTooth();
developer.BuyTastiestCandy(shop);

// Asserting uses the exact same syntax as when configuring calls,
// no need to teach yourself another syntax.
A.CallTo(() => shop.BuyCandy(lollipop)).MustHaveHappened();




FakeItEasy
Mock object                               Object Expression
Mock<IShopDataAccess>()                   { new IShopDataAccess with
 .Setup(fun data ->                         member __.GetProductPrice(productId) =
 <@ data.GetProductPrice(any()) @>)           match productId with
   .Calls<int>(function                       | 1234 -> 45M
   | 1234 -> 45M                              | 2345 -> 15M
   | 2345 -> 15M                              | _ -> failwith "Unexpected"
   | productID -> failwith "Unexpected"     member __.Save(_,_) =
   )                                          failwith "Not implemented"
   .Create()                              }




F# Object Expressions
FOQ MOCKING
F# as a Testing Language
WTF
// Arrange
let xs =
   Mock<IList<char>>.With(fun xs ->
      <@ xs.Count --> 2
          xs.Item(0) --> '0'
          xs.Item(1) --> '1'
          xs.Contains(any()) --> true
          xs.RemoveAt(2) ==> System.ArgumentOutOfRangeException()
      @>
   )
// Assert
Assert.AreEqual(2, xs.Count)
Assert.AreEqual('0', xs.Item(0))
Assert.AreEqual('1', xs.Item(1))
Assert.IsTrue(xs.Contains('0'))
Assert.Throws<System.ArgumentOutOfRangeException>(fun () ->
   xs.RemoveAt(2)
)




Foq: IList<char>
var order =
   new Mock<IOrder>()
         .SetupProperties(new {
            Price = 99.99M,
            Quantity = 10,
            Side = Side.Bid,
            TimeInForce = TimeInForce.GoodTillCancel
         })
   .Create();
Assert.AreEqual(99.99M, order.Price);
Assert.AreEqual(10, order.Quantity);
Assert.AreEqual(Side.Bid, order.Side);
Assert.AreEqual(TimeInForce.GoodTillCancel,
order.TimeInForce);




Foq: Anonymous Type
let [<Test>] ``order sends mail if unfilled`` () =
    // setup data
    let order = Order("TALISKER", 51)
    let mailer = mock()
    order.SetMailer(mailer)
    // exercise
    order.Fill(mock())
    // verify
    verify <@ mailer.Send(any()) @> once




Foq: Type Inference
let [<Test>] ``verify sequence of calls`` () =
    // Arrange
    let xs = Mock.Of<IList<int>>()
    // Act
    xs.Clear()
    xs.Add(1)
    // Assert
    Mock.VerifySequence
        <@ xs.Clear()
           xs.Add(any()) @>




Foq Sequences
FOQ DEPLOYMENT
F# as a Testing language
Nuget Dowload   CodePlex Download




Deployment
FOQ API
F# as a testing language
Setup a mock method in C# with a lambda expression:

new Mock<IList<int>>()
 .Setup(x => x.Contains(It.IsAny<int>())).Returns(true)
 .Create();

Setup a mock method in F# with a Code Quotation:

Mock<System.Collections.IList>()
 .Setup(fun x -> <@ x.Contains(any()) @>).Returns(true)
 .Create()


LINQ or
Quotations
Fluent Interface or
Functions
FOQ IMPLEMENTATION
F# as a testing language
Moq                 FakeItEasy
Total 16454         Total 11550
{ or } 2481         { or } 2948
Blank 1933          Blank 1522
Null checks 163     Null checks 92
Comments 7426       Comments 2566
Useful lines 4451   Useful lines 4422




LOC: Moq vs FakeItEasy
Fock v0.1      Fock v0.2
127 Lines      200 Lines
• Interfaces   • Interfaces
• Methods      • Abstract Classes
• Properties   • Methods
               • Properties
               • Raise Exceptions




Fock (aka Foq)
Foq.fs           Foq.fs + Foq.Linq.fs
Total 666        Total 933




LOC: Foq 0.8.1
QUESTIONS
F# as a Testing Language
What The Foq?

Unit Testing with Foq

  • 1.
    Unit Testing withFoq @ptrelford on @c4fsharp March 2013 Go download on Nuget or foq.codeplex.com
  • 2.
  • 3.
    what should alanguage for writing acceptance tests be? A Language for Testing
  • 4.
    Tests operate byexample they describe specific scenarios and responses A Language for Testing
  • 5.
    I wonder ifa different kind of programming language is required. - Martin Fowler 2003 A Language for Testing
  • 6.
    UNIT TESTING WITHF# F# as a Testing Language
  • 7.
    F# NUnit C# NUnit module MathTest = using NUnit.Framework; open NUnit.Framework [TestFixture] public class MathTest { let [<Test>] ``2 + 2 should equal 4``() = [Test] Assert.AreEqual(2 + 2, 4) public void TwoPlusTwoShouldEqualFour() { Assert.AreEqual(2 + 2, 4); } } NUnit
  • 8.
    let [<Test>] ``2+ 2 should equal 4``() = 2 + 2 |> should equal 4 FsUnit
  • 9.
    let [<Test>] ``2+ 2 should equal 4``() = test <@ 2 + 2 = 4 @> Unquote
  • 10.
    .NET MOCKING F# asa Testing Language
  • 11.
  • 12.
  • 13.
    var mock =new Mock<ILoveThisFramework>(); // WOW! No record/replay weirdness?! :) mock.Setup(framework => framework.DownloadExists("2.0.0.0")) .Returns(true) .AtMostOnce(); // Hand mock.Object as a collaborator and exercise it, // like calling methods on it... ILoveThisFramework lovable = mock.Object; bool download = lovable.DownloadExists("2.0.0.0"); // Verify that the given method was indeed called with the expected value mock.Verify(framework => framework.DownloadExists("2.0.0.0")); Moq
  • 14.
    // Creating afake object is just dead easy! // No mocks, no stubs, everything's a fake! var lollipop = A.Fake<ICandy>(); var shop = A.Fake<ICandyShop>(); // To set up a call to return a value is also simple: A.CallTo(() => shop.GetTopSellingCandy()).Returns(lollipop); // Use your fake as you would an actual instance of the faked type. var developer = new SweetTooth(); developer.BuyTastiestCandy(shop); // Asserting uses the exact same syntax as when configuring calls, // no need to teach yourself another syntax. A.CallTo(() => shop.BuyCandy(lollipop)).MustHaveHappened(); FakeItEasy
  • 15.
    Mock object Object Expression Mock<IShopDataAccess>() { new IShopDataAccess with .Setup(fun data -> member __.GetProductPrice(productId) = <@ data.GetProductPrice(any()) @>) match productId with .Calls<int>(function | 1234 -> 45M | 1234 -> 45M | 2345 -> 15M | 2345 -> 15M | _ -> failwith "Unexpected" | productID -> failwith "Unexpected" member __.Save(_,_) = ) failwith "Not implemented" .Create() } F# Object Expressions
  • 16.
    FOQ MOCKING F# asa Testing Language
  • 17.
  • 18.
    // Arrange let xs= Mock<IList<char>>.With(fun xs -> <@ xs.Count --> 2 xs.Item(0) --> '0' xs.Item(1) --> '1' xs.Contains(any()) --> true xs.RemoveAt(2) ==> System.ArgumentOutOfRangeException() @> ) // Assert Assert.AreEqual(2, xs.Count) Assert.AreEqual('0', xs.Item(0)) Assert.AreEqual('1', xs.Item(1)) Assert.IsTrue(xs.Contains('0')) Assert.Throws<System.ArgumentOutOfRangeException>(fun () -> xs.RemoveAt(2) ) Foq: IList<char>
  • 19.
    var order = new Mock<IOrder>() .SetupProperties(new { Price = 99.99M, Quantity = 10, Side = Side.Bid, TimeInForce = TimeInForce.GoodTillCancel }) .Create(); Assert.AreEqual(99.99M, order.Price); Assert.AreEqual(10, order.Quantity); Assert.AreEqual(Side.Bid, order.Side); Assert.AreEqual(TimeInForce.GoodTillCancel, order.TimeInForce); Foq: Anonymous Type
  • 20.
    let [<Test>] ``ordersends mail if unfilled`` () = // setup data let order = Order("TALISKER", 51) let mailer = mock() order.SetMailer(mailer) // exercise order.Fill(mock()) // verify verify <@ mailer.Send(any()) @> once Foq: Type Inference
  • 21.
    let [<Test>] ``verifysequence of calls`` () = // Arrange let xs = Mock.Of<IList<int>>() // Act xs.Clear() xs.Add(1) // Assert Mock.VerifySequence <@ xs.Clear() xs.Add(any()) @> Foq Sequences
  • 22.
    FOQ DEPLOYMENT F# asa Testing language
  • 23.
    Nuget Dowload CodePlex Download Deployment
  • 24.
    FOQ API F# asa testing language
  • 25.
    Setup a mockmethod in C# with a lambda expression: new Mock<IList<int>>() .Setup(x => x.Contains(It.IsAny<int>())).Returns(true) .Create(); Setup a mock method in F# with a Code Quotation: Mock<System.Collections.IList>() .Setup(fun x -> <@ x.Contains(any()) @>).Returns(true) .Create() LINQ or Quotations
  • 26.
  • 27.
    FOQ IMPLEMENTATION F# asa testing language
  • 28.
    Moq FakeItEasy Total 16454 Total 11550 { or } 2481 { or } 2948 Blank 1933 Blank 1522 Null checks 163 Null checks 92 Comments 7426 Comments 2566 Useful lines 4451 Useful lines 4422 LOC: Moq vs FakeItEasy
  • 29.
    Fock v0.1 Fock v0.2 127 Lines 200 Lines • Interfaces • Interfaces • Methods • Abstract Classes • Properties • Methods • Properties • Raise Exceptions Fock (aka Foq)
  • 30.
    Foq.fs Foq.fs + Foq.Linq.fs Total 666 Total 933 LOC: Foq 0.8.1
  • 31.
    QUESTIONS F# as aTesting Language
  • 32.

Editor's Notes

  • #5 http://martinfowler.com/bliki/TestingLanguage.html
  • #12 http://nugetmusthaves.com/Tag/mocking