Unit Testing with Foq@ptrelford on @c4fsharp March 2013
Go download on Nuget or foq.codeplex.com
Testing Language
A Language for Testing
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
UNIT TESTING WITH F#F# as a Testing Language
NUnit
F# NUnitmodule MathTest =
open NUnit.Framework
let [<Test>] ``2 + 2 should equal 4``() = Assert.AreEqual(2 + 2, 4)
C# NUnitusing NUnit.Framework;
[TestFixture]public class MathTest{ [Test] public void TwoPlusTwoShouldEqualFour() { Assert.AreEqual(2 + 2, 4); }}
FsUnit
let [<Test>] ``2 + 2 should equal 4``() = 2 + 2 |> should equal 4
Unquote
let [<Test>] ``2 + 2 should equal 4``() = test <@ 2 + 2 = 4 @>
.NET MOCKINGF# as a Testing Language
Mocking libraries
unittesting
Moq
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 valuemock.Verify(framework => framework.DownloadExists("2.0.0.0"));
FakeItEasy
// 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();
F# Object Expressions
Mock objectMock<IShopDataAccess>() .Setup(fun data -> <@ data.GetProductPrice(any()) @>) .Calls<int>(function | 1234 -> 45M | 2345 -> 15M | productID -> failwith "Unexpected" ) .Create()
Object Expression{ new IShopDataAccess with member __.GetProductPrice(productId) = match productId with | 1234 -> 45M | 2345 -> 15M | _ -> failwith "Unexpected" member __.Save(_,_) = failwith "Not implemented"}
FOQ MOCKINGF# as a Testing Language
WTF
Foq: IList<char>
// Arrangelet 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() @> )// AssertAssert.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: Anonymous Type
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: Type Inference
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 Sequences
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 DEPLOYMENTF# as a Testing language
Deployment
Nuget DowloadCodePlex Download
FOQ APIF# as a testing language
LINQ or Quotations
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()
Fluent Interface orFunctions
FOQ IMPLEMENTATIONF# as a testing language
LOC: Moq vs FakeItEasy
Moq
Total 16454
{ or } 2481
Blank 1933
Null checks 163
Comments 7426
Useful lines 4451
FakeItEasy
Total 11550
{ or } 2948
Blank 1522
Null checks 92
Comments 2566
Useful lines 4422
Fock (aka Foq)
Fock v0.1
127 Lines• Interfaces• Methods• Properties
Fock v0.2
200 Lines• Interfaces• Abstract Classes• Methods• Properties• Raise Exceptions
LOC: Foq 0.8.1
Foq.fs
Total 666
Foq.fs + Foq.Linq.fs
Total 933
QUESTIONSF# as a Testing Language
What The Foq?