Permalink
Browse files

Added naive, DI-like implementation

  • Loading branch information...
1 parent 7bdf3e3 commit d2387cceb81eabc349a63ab7df1249236e9b1d13 @ploeh committed Dec 10, 2016
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+ <runtime>
+ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+ <dependentAssembly>
+ <Paket>True</Paket>
+ <assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.4.0.0" />
+ </dependentAssembly>
+ </assemblyBinding>
+ </runtime>
+</configuration>
@@ -33,18 +33,6 @@
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\BookingApi.XML</DocumentationFile>
</PropertyGroup>
- <ItemGroup>
- <Reference Include="mscorlib" />
- <Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
- <Private>True</Private>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Numerics" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="AssemblyInfo.fs" />
- </ItemGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
@@ -251,4 +239,20 @@
</ItemGroup>
</When>
</Choose>
+ <ItemGroup>
+ <Content Include="App.config" />
+ <Compile Include="AssemblyInfo.fs" />
+ <Compile Include="MaîtreD.fs" />
+ <Compile Include="Composition.fs" />
+ <Compile Include="MaîtreDTests.fs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="mscorlib" />
+ <Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Numerics" />
+ </ItemGroup>
</Project>
@@ -0,0 +1,17 @@
+module Ploeh.Samples.Composition
+
+// Dirty, lying module that pretends to implement real SQL operations
+module DB =
+ let readReservations connectionString date = []
+ let createReservation connectionString reservation = 0
+
+open MaîtreD
+
+// More pretension
+let connectionString = ""
+
+// Reservation -> int option
+let tryAcceptComposition reservation =
+ let read = DB.readReservations connectionString
+ let create = DB.createReservation connectionString
+ tryAccept 10 read create reservation
@@ -0,0 +1,18 @@
+module Ploeh.Samples.MaîtreD
+
+open System
+
+type Reservation = {
+ Date : DateTimeOffset
+ Quantity : int
+ IsAccepted : bool
+}
+
+// int -> (DateTimeOffset -> Reservation list) -> (Reservation -> int) -> Reservation
+// -> int option
+let tryAccept capacity readReservations createReservation reservation =
+ let reservedSeats =
+ readReservations reservation.Date |> List.sumBy (fun x -> x.Quantity)
+ if reservedSeats + reservation.Quantity <= capacity
+ then createReservation { reservation with IsAccepted = true } |> Some
+ else None
@@ -0,0 +1,58 @@
+module Ploeh.Samples.MaîtreDTests
+
+open Ploeh.Samples.MaîtreD
+open FsCheck
+open FsCheck.Xunit
+open Swensen.Unquote
+
+// 'Guard' composition. Returns the return value if ``assert`` doesn't throw.
+let (>>!) ``assert`` returnValue x =
+ ``assert`` x
+ returnValue
+
+module Tuple2 =
+ let curry f x y = f (x, y)
+
+module Gen =
+ let reservation = gen {
+ let! PositiveInt quantity = Arb.generate
+ let! reservation = Arb.generate
+ return { reservation with Quantity = quantity } }
+
+[<Property(QuietOnSuccess = true)>]
+let ``tryAccept behaves correctly when it can accept``
+ (NonNegativeInt excessCapacity)
+ (expected : int) =
+ Tuple2.curry id
+ <!> Gen.reservation
+ <*> Gen.listOf Gen.reservation
+ |> Arb.fromGen |> Prop.forAll <| fun (reservation, reservations) ->
+ let capacity =
+ excessCapacity
+ + (reservations |> List.sumBy (fun x -> x.Quantity))
+ + reservation.Quantity
+ let readReservations = ((=!) reservation.Date) >>! reservations
+ let createReservation =
+ ((=!) { reservation with IsAccepted = true }) >>! expected
+
+ let actual =
+ tryAccept capacity readReservations createReservation reservation
+
+ Some expected =! actual
+
+[<Property(QuietOnSuccess = true)>]
+let ``tryAccept behaves correctly when it can't accept``
+ (PositiveInt lackingCapacity) =
+ Tuple2.curry id
+ <!> Gen.reservation
+ <*> Gen.listOf Gen.reservation
+ |> Arb.fromGen |> Prop.forAll <| fun (reservation, reservations) ->
+ let capacity =
+ (reservations |> List.sumBy (fun x -> x.Quantity)) - lackingCapacity
+ let readReservations _ = reservations
+ let createReservation _ = failwith "Mock shouldn't be called."
+
+ let actual =
+ tryAccept capacity readReservations createReservation reservation
+
+ None =! actual
View
@@ -1,12 +1,18 @@
#r @"packages/FAKE/tools/FakeLib.dll"
open Fake
+open Fake.Testing
Target "Build" <| fun _ ->
!! "**/BookingApi.sln"
|> MSBuildRelease "" "Rebuild"
|> ignore
+Target "Test" <| fun _ ->
+ !! "*/bin/Release/Ploeh.Samples.*.dll"
+ |> xUnit2 (fun p -> { p with Parallel = ParallelMode.All })
+
"Build"
+==> "Test"
-RunTargetOrDefault "Build"
+RunTargetOrDefault "Test"

0 comments on commit d2387cc

Please sign in to comment.